• C和指针 第9章 字符串、字符和字节 9.14 编程练习


    1. 编写一个程序,从标准输入读取一些字符,并统计下列各类字符所占的百分比。
        控制字符
        空白字符
        数字
        小写字母
        大写字母
        标点符号
        不可打印的字符
    请使用在ctype.h头文件中定义的字符分类函数:
    /*
    ** program_1_character_classification 
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>

    int main( void ){
        int ch; 
        int cntrl = 0;
        int space = 0;
        int digit = 0;
        int lower = 0;
        int upper = 0;
        int punct = 0;
        int unprint = 0;
        int total = 0;
        
        while( (ch = getchar()) != EOF ){
            if( iscntrl( ch ) ){
                cntrl++;
            }
            if( isspace( ch ) ){
                space++;
            }
            if( isdigit( ch ) ){
                digit++;
            }
            if( islower( ch ) ){
                lower++;
            }
            if( isupper( ch ) ){
                upper++;
            }
            if( ispunct( ch ) ){
                punct++;
            }
            if( !isprint( ch ) ){
                unprint++;
            }
            total++;
        }
        double cntrl_pec, space_pec, digit_pec, lower_pec, upper_pec, punct_pec, unprint_pec;
        cntrl_pec = (double)cntrl / total * 100;
        space_pec = (double)space / total * 100;
        digit_pec = (double)digit / total * 100;
        lower_pec = (double)lower / total * 100;
        upper_pec = (double)upper / total * 100;
        punct_pec = (double)punct / total * 100;
        unprint_pec = (double)unprint / total;
        printf( "cntrl_pec = %.2lf%%, space_pec = %.2lf%%, digit_pec = %.2lf%%,\n"
        "lower_pec = %.2lf%%, upper_pec = %.2lf%%, punct_pec = %.2lf%%, unprint_pec = %.2lf%%\n",
        cntrl_pec, space_pec, digit_pec, lower_pec, upper_pec, punct_pec, unprint_pec );    

        return EXIT_SUCCESS;
    }
    /* 输出:

    */ 
    2. 编写一个名叫my_strlen的函数。它类似于strlen函数,但它能够处理由于使用strn---函数而创建的未以NUL
    字节结尾的字符串。需要向函数传递一个参数,它的值就是这样一个数组的长度,即这个数组保存了需要进行
    长度测试的字符串。
    解析:
    非常不幸!标准函数库并没有提供这个函数。
    /*
    ** 安全的字符串长度函数。它返回一个字符串的长度,即使字符串并未以NUL字节结尾。
    ** 'size'是存储字符串的缓冲区的长度。 
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stddef.h> 

    #define ARR_SIZE 10
    size_t my_strlen( char const *string, int size );

    int main( void ){
        char string[ARR_SIZE];
        char string2[ARR_SIZE];
        char string3[ARR_SIZE];
        char string4[ARR_SIZE];
        char string5[ARR_SIZE];
        char string6[ARR_SIZE];
        char string7[ARR_SIZE * 2];
        size_t str_len;
        
        printf( "condition: the length of string src is more than or equal to len:\n" );
        printf( "the size of dst is smaller than len:\n" );
        /*
        ** the string stored in dst is more than the size limit of dst, 
        ** so the result of my_strlen function is wrong.
        */
        /*
        ** wrong answer.
        */
        strncpy( string, "abcdefghijk", ARR_SIZE + 1 );
        printf( "string = %s\n", string );
        str_len = my_strlen( string, ARR_SIZE ); 
        printf( "str_len = %zd\n", str_len );
        /*
        ** the string stored in dst is smaller than or equal to the size limit of dst, 
        ** so the result of my_strlen is correct. When the number of string stored in dst 
        ** is equal to len, the string stored in dst doesn't ends with a '\0', otherwise 
        ** ends with a '\0'.
        */
        /*
        ** correct answer.
        */
        printf( "the size of dst is equal to len:\n" );
        strncpy( string2, "abcdefghijk", ARR_SIZE );
        printf( "string2 = %s\n", string2 );
        str_len = my_strlen( string2, ARR_SIZE ); 
        printf( "str_len = %zd\n", str_len );
        /*
        ** correct answer.
        */
        printf( "the size of dst is more than len:\n" );
        strncpy( string3, "abcdefghijk", ARR_SIZE - 1 );
        printf( "string3 = %s\n", string3 );
        str_len = my_strlen( string3, ARR_SIZE ); 
        printf( "str_len = %zd\n", str_len );
        
        printf( "condition2: the length of string src is smaller than len:\n" );
        printf( "the size of dst is smaller than len:\n" );
        /*
        ** the string stored in dst is more than the size limit of dst, 
        ** if the size of dst is smaller than the length of string stored in src, dst[ARR_SIZE-1]
        ** is not '\0', the reasult of my_strlen is wrong. If the size of dst is equal to the length of 
        ** string stored in src, dst[ARR_SIZE-1] is not also '\0', but the result of my_strlen is correct,
        ** If the size of dst is more than the length of string stored in src, dst[ARR_SIZE-1] is '\0'.
        */
        /*
        ** wrong answer.
        */
        printf( "the size of dst is smaller than the length of the string stored in the src:\n" );
        strncpy( string4, "abcdefghijk", ARR_SIZE + 2 );
        printf( "string4 = %s\n", string4 );
        str_len = my_strlen( string4, ARR_SIZE ); 
        printf( "str_len = %zd\n", str_len );
        /*
        ** correct answer.
        */
        printf( "the size of dst is equal to the length of the string stored in the src:\n" );
        strncpy( string5, "abcdefghij", ARR_SIZE + 2 );
        printf( "string5 = %s\n", string5 );
        str_len = my_strlen( string5, ARR_SIZE );
        printf( "str_len = %zd\n", str_len );
        /*
        ** correct answer.
        */
        printf( "the size of dst is larger than the length of the string stored in the src:\n" );
        strncpy( string6, "abcdefghi", ARR_SIZE + 2 );
        printf( "string6 = %s\n", string6 );
        str_len = my_strlen( string6, ARR_SIZE );
        printf( "str_len = %zd\n", str_len );
        
        printf( "the size of dst is more than or equal to len:\n" );
        /*
        ** the string stored in dst is smaller than the size limit of string, 
        ** so the result of my_strlen is always correct. dst[ARRSIZE-1] is always '\0'.
        */
        strncpy( string7, "abcdefghi", ARR_SIZE + 2 );
        printf( "string7 = %s\n", string7 );
        str_len = my_strlen( string7, ARR_SIZE * 2 );
        printf( "str_len = %zd\n", str_len );

        return EXIT_SUCCESS;
    }

    size_t my_strlen( char const *string, int size ){
        register size_t length;
        
        for( length = 0; length < size; length += 1 ){
            if( *string++ == '\0' ){
                break;
            }
        }
        return length;
    }

    /* 输出:

    */ 
    3. 编写一个名叫my_strcpy的函数,它类似于strcpy函数,但不会溢出目标数组。复制的结果必须是一个真正的字符串。 

    /*
    ** program_3_my_strcpy.cpp
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #define ARR_SIZE 10
    /* 
    ** If elements copyed is more than or equal to dst_size,
    ** this function only copy dst_size - 1 elements to dst.
    ** Otherwise copy copy_size elements to dst.
    */
    char *my_strcpy( char *dst, char const *src, size_t dst_size, size_t copy_size );

    int main( void ){
        char dst[ARR_SIZE];
        char const *src = "hello";
        
        my_strcpy( dst, src, ARR_SIZE, strlen( src ) );
        printf( "after my_strcpy( dst, \"%s\", ARR_SIZE, strlen( \"%s\" ) ), dst = %s\n", src, src, dst ); 

        return EXIT_SUCCESS;
    }

    char *my_strcpy( char *dst, char const *src, size_t dst_size, size_t copy_size ){
        int arr_size;
        int i;
        int counter;
        const char *psrc = src;
        /*
        ** compute the character number in the src.
        */
        counter = 0;
        while( *psrc++ ){
            counter++;
        }
        /*
        ** update the value of copy_size.
        */
        if( counter < copy_size ){
            copy_size = counter;
        }
        /*
        ** compute the number of value copyed.
        */
        if( dst_size <= copy_size ){
            arr_size = dst_size - 1;
        } else{
            arr_size = copy_size;
        }
        char *pdst = dst;
        for( i = 0; i < arr_size; ++i ){
            *pdst++ = *src++;
        }
        *pdst = '\0';
        
        return dst;
    }

    /* 输出:

    */ 
    4. 编写一个名叫my_strcat的函数。它类似于strcat函数,但不会溢出目标数组。它的结果必须是一个真正的字符串。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stddef.h>

    #define ARR_SIZE 10
    /*
    ** If the length of the length of the string stored in dst adds copy_size is more than and equal to dst_size, 
    ** the copy_size is changed into the value of dst_size substracts the sum of the length of the string stored 
    ** in dst and one. because '\0' needs to occupy one byte in dst.
    */
    char *my_strcat( char *dst, char const *src, size_t dst_size, size_t strcat_size );
     
    int main( void ){
        char dst[ARR_SIZE] = "hello" ;
        char const *src = ",world!";
        
        my_strcat( dst, src, ARR_SIZE, strlen( src ) );
        printf( "after my_strcat( dst, \"%s\", ARR_SIZE, strlen( \"%s\" ) ), dst = %s\n", src, src, dst );

        return EXIT_SUCCESS;
    }

    char *my_strcat( char *dst, char const *src, size_t dst_size, size_t strcat_size ){
        size_t dst_counter;
        char *pdst;
        
        /*
        ** compute the length of string stored in dst.
        */
        pdst = dst;
        dst_counter = 0;
        while( *pdst++ ){
            dst_counter++;
        }
        /*
        ** the reason of pdst-- is *pdst++,
        ** leads to pdst add one again.
        */ 
        pdst--;
        /*
        ** update the copy_size to make dst because a string.
        */
        if( dst_counter + strcat_size - 1 > dst_size ){
            strcat_size = dst_size - dst_counter - 1;
        } 
        printf( "strcat_size = %d\n", strcat_size );
        /*
        ** concatenate strcat_size elements in src to the behind of the end of dst. 
        */
        int i;
        for( i = 0; i < strcat_size; ++i ){
            *pdst = *src;
            src++;
            pdst++;
        }
        *pdst = '\0';
        
        return dst;
    }
    /* 输出:

    */
    5. 编写下面的函数:
        void my_strncat( char *dest, char *src, int dest_len );
    它用于把src中的字符串连接到dest中原有字符串的末尾,但保证不会溢出长度为dest_len的dest数组。和strncat函数不同,这个函数也会考虑原先存在于dest数组的字符串长度,因此能够保证不会超越数组边界。 
    #include <stdio.h>
    #include <stdlib.h>

    #define ARR_SIZE 12

    void my_strncat( char *dest, char *src, int dest_len );

    int main( void ){
        char dst[ARR_SIZE] = "hello" ;
        char src[] = ",world!";
        
        my_strncat( dst, src, ARR_SIZE );
        printf( "after my_strncat( dst, \"%s\", %d ), dst = %s\n", src, ARR_SIZE, dst );

        return EXIT_SUCCESS;
    }

    void my_strncat( char *dest, char *src, int dest_len ){
        size_t dst_counter;
        char *pdst;
        
        /*
        ** compute the length of string stored in dst.
        */
        pdst = dest;
        dst_counter = 0;
        while( *pdst++ ){
            dst_counter++;
        }
        /*
        ** the reason of pdst-- is *pdst++,
        ** leads to pdst add one again.
        */ 
        pdst--;
        /*
        ** concatenate elements in src to the behind of the end of dst. 
        */
        for( ; dst_counter < dest_len - 1; ){
            *pdst = *src;
            src++;
            pdst++;
            dst_counter++;
        }
        printf( "dest = %s\n", dest );
        *pdst = '\0';
    }

    /* 输出:

    */ 
    6. 编写一个名叫my_strcpy_end的函数,用来取代strcpy函数,它返回一个指向目标字符串末尾的指针(也就是说,指向NUL字节的指针),而不是返回一个指向目标字符串起始位置的指针。
    解析:
    这个问题有两种解决方法。第1种是简单但效率稍差的方案。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    /*
    ** 字符串复制函数,返回一个指向目标参数末尾的指针(版本1)。 
    */ 
    char *my_strcpy_end( char *dst, char const *src ); 
    /*
    ** 用这种方法解决问题,最后一次调用strlen函数所消耗的时间不会少于省略那个字符串连接函数所节省的时间。 
    */
    /* 第2种方法避免使用库函数。register声明用于提高函数的效率。*/
    /*
    ** 字符拷贝函数,返回一个指向目标参数末尾的指针,不使用任何标准库字符处理函数(版本2) 
    */ 
    char *my_strcpy_end2( register char *dst, register char const *src ); 
    /*
    **用这个方案解决问题并没有充分利用有些实现了特殊的字符串处理指令的机器所提供的的额外效率。 
    */

    int main( void ){
        char dst[100];
        char src[] = "hello, world!";
        char *pdst;
        size_t src_len;
        
        src_len = strlen( src );
        pdst = my_strcpy_end( dst, src );
        printf( "after pdst = my_strcpy_end( dst, \"%s\" ), pdst = %p, dst + src_len = %p\n", src, pdst, dst + src_len );
        
        pdst = my_strcpy_end2( dst, src );
        printf( "after pdst = my_strcpy_end2( dst, \"%s\" ), pdst = %p, dst + src_len = %p\n", src, pdst, dst + src_len );

        return EXIT_SUCCESS;
    }

    char *my_strcpy_end( char *dst, char const *src ){
        strcpy( dst, src );
        return dst + strlen( dst );
    }

    char *my_strcpy_end2( register char *dst, register char const *src ){
        while( (*dst++ = *src++) != '\0' ){
            ;
        }
        return dst - 1;
    }
    /* 输出:

    */ 
    7. 编写一个名叫my_strrchr的函数,它的原型如下:
        char *my_strrchr( char const *str, int ch );
    这个函数类似于strchr函数,只是它返回的是一个指向ch字符在str字符串中最后一次出现(最右边)的位置的指针。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char *my_strrchr( char const *str, int ch );

    int main( void ){
        char const *str = "hello, world!";
        int ch = 'o';
        char *pstr;
        char *pstr2;
        
        pstr = my_strrchr( str, ch );
        pstr2 = strrchr( str, ch );
        printf( "after pstr = my_strrchr( \"%s\", %c ), pstr = %p, *pstr = %c\n""pstr2 = %p, *pstr2 = %c\n",
        str, ch, pstr, *pstr, pstr2, *pstr2 );

        return EXIT_SUCCESS;
    }

    char *my_strrchr( char const *str, int ch ){
        register char *last;
        register char *current;
        /*
        ** initialize this pointer to previous matched location has been found. 
        */ 
        last = NULL;
        /*
        ** search the first appearance of ch in the str. 
        */
        current = strchr( str, ch );
        if( current != NULL ){
            /*
            ** when find ch every time, make last points to the location of ch, 
            ** and then search next matched location of ch.
            */
            while( current != NULL ){
                last = current;
                current = strchr( last + 1, ch );
            }
        }
        /* return the pointer of eventual matched location */
        return last; 
    }
    /* 输出:

    */ 
    8. 编写一个名叫my_strnchr的函数,它的原型如下:
        char *my_strnchr( char const *str, int ch, int which );
    这个函数类似于strchr函数,但它的第3个参数指定ch字符在str字符串中第几次出现。例如,如果第3个参数为1,这个函数的功能就和strchr完全一样。如果第3个参数为2,这个函数就返回一个指向ch字符在str字符中第2次出现的位置的指针。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char *my_strnchr( char const *str, int ch, int which );

    int main( void ){
        char const *str = "hello, world!";
        int ch = 'o';
        int which;
        int which2;
        char *pstr;
        char *pstr2;
        
        which = 1;
        pstr = my_strnchr( str, ch, which );
        printf( "after pstr = my_strnchr( \"%s\", %c, %d ), pstr = %p, *pstr = %c\n", str, ch, which, pstr, *pstr ); 
        which2 = 2;
        pstr2 = my_strnchr( str, ch, which2 );
        printf( "after pstr2 = my_strnchr( \"%s\", %c, %d ), pstr2 = %p, *pstr2 = %c\n", str, ch, which2, pstr2, *pstr2);

        return EXIT_SUCCESS;
    }

    char *my_strnchr( char const *str, int ch, int which ){
        register char *last;
        register char *current;
        int counter = 0; 
        /*
        ** initialize this pointer to previous matched location has been found. 
        */ 
        last = NULL;
        /*
        ** search the first appearance of ch in the str. 
        */
        current = strchr( str, ch );
        if( current != NULL ){
            /*
            ** when find ch every time, make last points to the location of ch, 
            ** execute counter++ operation. If counter is smaller than which, 
            ** then search next matched location of ch.
            */
            last = current;
            counter++;
            while( counter < which ){
                current = strchr( last + 1, ch );
                if( current != NULL ){
                    last = current;
                    counter++;
                } else{
                    break;
                }
            }
        }
        /* return the pointer of the ch_th matched location */
        return last; 
    }

    /* 输出:

    */
    9. 编写一个函数,它的原型如下:
        int count_chars( char const *str, char const *chars );
    函数应该在第1个参数中进行查找,并返回匹配第2个参数所包含的字符的数量。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    int count_chars( char const *str, char const *chars );

    int main( void ){
        char const *str = "abcdefghijklmnopqrstuvwxyz";
        char const *chars = "abcdefghijklmnopqrstuvwxyzabc";
        int counter;
        
        counter = count_chars( str, chars );
        printf( "counter = %d\n", counter );

        return EXIT_SUCCESS;
    }

    int count_chars( char const *str, char const *chars ){
        int counter = 0;
        char *pstr;
        
        while( *chars ){
            pstr = strchr( str, *chars );
            if( pstr ){
                counter++;
            }
            chars++;
        } 
        return counter;
    }
    /* 输出:

    */ 
    10. 编写函数
        int palindrome( char *string );
    如果参数字符串是个回文,函数就返回真,否则返回假。回文就是指一个字符串从左向右读和从右往左读是一样的。函数应该忽略所有的非字母字符,而且在进行字符比较时不用区分大小写。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>

    #define TRUE 1
    #define FALSE 0
    int palindrome( char *string );

    int main( void ){
        char string[] = "sheeeeHS";
        char string2[] = "Man";
        int result;
        int result2;
        char *p;
        
        p = string;
        while( *p ){
            *p = tolower( *p );
            *p++;
        }
        p = string2;
        while( *p ){
            *p = tolower( *p );
            *p++;
        }
        result = palindrome( string );
        if( result == TRUE ){
            printf( "%s is a palindrome.\n", string );
        } else{
            printf( "%s is not a palindrome.\n", string );
        }
        result2 = palindrome( string2 );
        if( result2 == TRUE ){
            printf( "%s is a palindrome.\n", string2 );
        } else{
            printf( "%s is not a palindrome.\n", string2 );
        }
        
        return EXIT_SUCCESS;
    }

    int palindrome( char *string ){
        size_t str_len;
        register char *p1, *p2;
        
        str_len = strlen( string );
        p1 = string;
        p2 = string + str_len - 1;
        while( p1 < p2 ){
            if( *p1 != *p2 ){
                return FALSE;
            }
            p1++;
            p2--;
        }
        return TRUE;
    }
    /* 输出:

    */ 
    11. 编写一个程序,对标准输入进行扫描,并对单词“the”出现的次数进行计数。进行比较时应该区分大小写,所以“The”和“THE”并不计算在内。我们可以认为各单词由一个或多个空格字符分隔,而且输入行在长度上不会超过100字符。计算结果应该写到标准输出上。
    解析:
    一个长度为101字节的缓冲区数组,用于保存100字节的输入和NUL终止符。strtok函数用于逐个提取单词。 
    /*
    ** 计算标准输入中单词“the”出现的次数。字母是区分大小写的,输入中的单词由一个或多次空白字符分隔。 
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char const whitespace[] = " \n\r\f\t\v"; 

    int main( void ){
        char buffer[101];
        int count;
        
        count = 0;
        
        /*
        ** 读入文本行,直到发现EOF。
        */
        while( gets( buffer ) ){
            char *word;
            
            /*
            ** 从缓冲区逐个提取单词,直到缓冲区内不再有单词。 
            */
            for( word = strtok( buffer, whitespace ); word != NULL; word = strtok( NULL, whitespace ) ){
                if( strcmp( word, "the" ) == 0 ){
                    count += 1;
                }
            } 
        }
        
        printf( "%d\n", count );
        
        return EXIT_SUCCESS; 

        return EXIT_SUCCESS;
    }
    /* 输出:

    */ 

    12. 有一种技巧可以对数据进行加密,并使用一个单词作为它的密钥。下面是它的工作原理:首先,选择一个单词作为密钥,如TRAILBLAZERS。如果单词中包含重复的字母,则只保留一个,其余几个丢弃。现在,修改过的那个单词列于字母表的下面,如下所示:
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    T R A I L B Z E S
    最后,底下那行用字母表中剩余的字母填充完整: 
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    T R A I L B Z E S C D F G H J K M N O P Q U V W X Y
    在对信息进行加密是,信息中的每个字母被固定于顶上那行,并用下面那行的对应字母一一取代原文的字母。因此,使用这个密钥,ATTACK AT DAWN(黎明时攻击)就会被加密为TPPTAD IP ITVH。
    这个题材共有3个程序(包括下面两个练习),在第一个程序中,需要编写函数:
        int prepare_key( char *key );
    它接受一个字符串参数,它的内容就是需要使用的密钥单词。函数根据上面描述的方法把它转换成一个包含编好码的字符数组。假定key参数是个字符数组,其长度至少可以容纳27字符。函数必须把密钥中的所有字符要么转换为大写字母,要么转换为小写字母(随意选择),并从单词中去除重复的字母,然后再用字母表中剩余的字母按照原先所选择的大小写形式填充到key数组中。如果处理成功,函数返回一个真值。如果key参数为空或者包含任何非字母字符,函数将返回一个假值。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>

    int prepare_key( char *key );
     
    int main( void ){    int result, result2;
        char key[27] = "TRAILBLAZERS";
        char key2[27] = "ATTACKATDAWN";
        
        printf( "original key = %s, key2 = %s\n", key, key2 );
        result = prepare_key( key );
        printf( "after result = prepare_key( key ), result = %d, key = %s\n", result, key );
        result2 = prepare_key( key2 );
        printf( "after result = prepare_key( key2 ), result2 = %d, key2 = %s\n", result2, key2 );

        return EXIT_SUCCESS;
    }

    int prepare_key( char *key ){
        /*
        ** key is NULL.
        ** 0 indicate a false value.
        */
        if( !key ){
            return 0;
        }
        /*
        ** *key is equal to '\0', return 0 directly.
        */
        if( *key == '\0' ){
            return 0;
        } 
        /*
        ** p is used to record the increased process of key.
        ** p2 is used to record the lastest location of new key.
        */
        char *p;
        char *p2;
        p = key;
        p2 = key;
        /*
        ** used to prompt the first element.
        */
        int first = 1;
        /*
        ** used to record the number of elements converted.
        */
        int counter = 0;
        while( *p ){
            /*
            ** Once any non-alpha element appears, return a flase value.
            */
            if( !isalpha( *p ) ){
                return 0;
            }
            /*
            ** convert the element to the uppercase form of element. 
            */
            *p = toupper( *p );
            /*
            ** used to store the first element.
            */
            if( first == 1 ){
                first = 0;
                *p2 = *p;
                p2++;
                counter++;
            } else{
                int i;
                /*
                ** In new key, If don't have new element, add new element to new key.
                */
                for( i = 0; i < counter; ++i ){
                    if( *p == *(key + i) ){
                        break;
                    }
                }
                if( i == counter ){
                    *p2++ = *p;
                    counter++;
                }
            }
            p++;
        }
        *p2 = '\0';
        /* printf( "key = %s\n", key ); */
        /*
        ** put extra new upper alphabet is not included in new key into new key.
        */
        char ch;
        for( ch = 'A'; ch <= 'Z'; ++ch ){
            char *ps = strchr( key, ch );
            if( !ps ){
                *p2 = ch;
                p2++;
                *p2 = '\0';
            }
        }
        return 1;
    }
    /* 输出:

    */
    13. 编写下面的函数:
        void encrypt( char *data, char const *key );
    它接受前题prepare_key函数所产生的密钥对data中的字符进行加密。data中的非字母不做修改,但字母字符则用密钥所提供的编码后的字符一一取代源字符。字母字符的大小写状态应该保留。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>

    int prepare_key( char *key );
    void encrypt( char *data, char const *key ); 

    int main( void ){
        int result, result2;
        char key[27] = "TRAILBLAZERS";
        char data[] = "ATTACK AT DAWN";
        
        printf( "original key = %s, data = %s\n", key, data );
        result = prepare_key( key );
        encrypt( data, key ); 
        printf( "after result = prepare_key( key ), encrypt( data, \"%s\" ),  result = %d\n", key, result );
        printf( "data = %s\n", data );

        return EXIT_SUCCESS;
    }

    int prepare_key( char *key ){
        /*
        ** key is NULL.
        ** 0 indicate a false value.
        */
        if( !key ){
            return 0;
        }
        /*
        ** *key is equal to '\0', return 0 directly.
        */
        if( *key == '\0' ){
            return 0;
        } 
        /*
        ** p is used to record the increased process of key.
        ** p2 is used to record the lastest location of new key.
        */
        char *p;
        char *p2;
        p = key;
        p2 = key;
        /*
        ** used to prompt the first element.
        */
        int first = 1;
        /*
        ** used to record the number of elements converted.
        */
        int counter = 0;
        while( *p ){
            /*
            ** Once any non-alpha element appears, return a flase value.
            */
            if( !isalpha( *p ) ){
                return 0;
            }
            /*
            ** convert the element to the uppercase form of element. 
            */
            *p = toupper( *p );
            /*
            ** used to store the first element.
            */
            if( first == 1 ){
                first = 0;
                *p2 = *p;
                p2++;
                counter++;
            } else{
                int i;
                /*
                ** In new key, If don't have new element, add new element to new key.
                */
                for( i = 0; i < counter; ++i ){
                    if( *p == *(key + i) ){
                        break;
                    }
                }
                if( i == counter ){
                    *p2++ = *p;
                    counter++;
                }
            }
            p++;
        }
        *p2 = '\0';
        /* printf( "key = %s\n", key ); */
        /*
        ** put extra new upper alphabet is not included in new key into new key.
        */
        char ch;
        for( ch = 'A'; ch <= 'Z'; ++ch ){
            char *ps = strchr( key, ch );
            if( !ps ){
                *p2 = ch;
                p2++;
                *p2 = '\0';
            }
        }
        return 1;
    }

    void encrypt( char *data, char const *key ){
        size_t data_len; 
        char *pdb;
        char *pde;
        
        data_len = strlen( data );
        pde = data + data_len;
        
        for( pdb = data; pdb < pde; ++pdb ){
            if( islower( *pdb ) ){
                *pdb = tolower( *(key + (*pdb - 'a')) );
            } else if( isupper( *pdb ) ){
                *pdb = *(key + (*pdb - 'A'));
            }
        }
    }
    /* 输出:

     */
    14. 这个问题的最后部分就是编写下面的函数:
        void decrypt( char *data, char const *key );
    它接受一个加过密的字符串参数,它的任务是重现原来的信息。除了它是用于解密之外,它的工作原理应该与encrypt相同。 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>

    int prepare_key( char *key );
    void encrypt( char *data, char const *key ); 
    void decrypt( char *data, char const *key ); 

    int main( void ){
        int result, result2;
        char key[27] = "TRAILBLAZERS";
        char endata[] = "ATTACK at DAWN";
        
        printf( "original key = %s, endata = %s\n", key, endata );
        result = prepare_key( key );
        encrypt( endata, key ); 
        printf( "after result = prepare_key( key ), encrypt( data, \"%s\" ),  result = %d\n", key, result );
        printf( "endata = %s\n", endata );
        decrypt( endata, key );
        printf( "after decrypt( dedata, \"%s\" ), dedata = %s\n", key, endata );

        return EXIT_SUCCESS;
    }

    int prepare_key( char *key ){
        /*
        ** key is NULL.
        ** 0 indicate a false value.
        */
        if( !key ){
            return 0;
        }
        /*
        ** *key is equal to '\0', return 0 directly.
        */
        if( *key == '\0' ){
            return 0;
        } 
        /*
        ** p is used to record the increased process of key.
        ** p2 is used to record the lastest location of new key.
        */
        char *p;
        char *p2;
        p = key;
        p2 = key;
        /*
        ** used to prompt the first element.
        */
        int first = 1;
        /*
        ** used to record the number of elements converted.
        */
        int counter = 0;
        while( *p ){
            /*
            ** Once any non-alpha element appears, return a flase value.
            */
            if( !isalpha( *p ) ){
                return 0;
            }
            /*
            ** convert the element to the uppercase form of element. 
            */
            *p = toupper( *p );
            /*
            ** used to store the first element.
            */
            if( first == 1 ){
                first = 0;
                *p2 = *p;
                p2++;
                counter++;
            } else{
                int i;
                /*
                ** In new key, If don't have new element, add new element to new key.
                */
                for( i = 0; i < counter; ++i ){
                    if( *p == *(key + i) ){
                        break;
                    }
                }
                if( i == counter ){
                    *p2++ = *p;
                    counter++;
                }
            }
            p++;
        }
        *p2 = '\0';
        /* printf( "key = %s\n", key ); */
        /*
        ** put extra new upper alphabet is not included in new key into new key.
        */
        char ch;
        for( ch = 'A'; ch <= 'Z'; ++ch ){
            char *ps = strchr( key, ch );
            if( !ps ){
                *p2 = ch;
                p2++;
                *p2 = '\0';
            }
        }
        return 1;
    }

    void encrypt( char *data, char const *key ){
        size_t data_len; 
        char *pdb;
        char *pde;
        
        data_len = strlen( data );
        pde = data + data_len;
        
        for( pdb = data; pdb < pde; ++pdb ){
            if( islower( *pdb ) ){
                *pdb = tolower( *(key + (*pdb - 'a')) );
            } else if( isupper( *pdb ) ){
                *pdb = *(key + (*pdb - 'A'));
            }
        }
    }

    void decrypt( char *data, char const *key ){
        size_t data_len; 
        char *pdb;
        char *pde;
        
        data_len = strlen( data );
        pde = data + data_len;
        
        for( pdb = data; pdb < pde; ++pdb ){
            if( islower( *pdb ) ){
                *pdb = (strchr( key , toupper( *pdb ) ) - key) + 'a';
            } else if( isupper( *pdb ) ){
                *pdb = (strchr( key , *pdb ) - key) + 'A';
            }
        }
    }
    /* 输出:

    */ 
    15. 标准I/O库并没有提供一种机制,在打印大整数时用逗号进行分隔。在这个练习中,需要编写一个程序,为美元数额的打印提供这个功能。函数将把一个数字字符串(代表以美分为单位的金额)转换为美元形式,如下面的例子所示:
        输入    输出       输入             输出
        空        $0.00     12345         $123.45    
        1          $0.01    123456        $1,234.56
        12        $0.12    1234567      $12,345.67
        123      $1.23    12345678    $123,456.78
        1234    $12.34   123456789 $1,234,567.89
    下面是函数的原型:
        void dollars( char *dest, char const *src );
    src将指向需要被格式化的字符(可以假定它们都是数字)。函数应该像上面的例子所示的那样对字符进行格式化,并把结果字符串保存到dest中。应该保证所创建的字符串以一个NUL字节结尾。src的值不应该被修改。应该使用指针而不是下标。 
    提示:首先找到第2个参数字符串的长度。这个值有助于判断逗号应插入到什么位置。同时,小数点和最后两位数字应该是唯一需要处理的特殊情况。 
    解析:
    尽管没有在规范中说明,但这个函数应该对两个参数都进行检查,确保它们不是NULL。程序包含了stdio.文件,因为它定义了NULL。如果参数能够通过测试,我们只能假定输入字符串已被正确地加上了终止符。
    /*
    ** 把数字字符串'src'转换为美元和美分的形式,并存储于'dst'。 
    */ 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    void dollars( char *dest, char const *src );

    int main( void ){
        char dest[20];
        char const src[] = "";
        char const src2[] = "123456";
        
        dollars( dest, src );
        printf( "after dollars( dest, \"%s\" ), src = %s, dest = %s\n", src, src, dest );
        
        dollars( dest, src2 );
        printf( "after dollars( dest, \"%s\" ), src2 = %s, dest = %s\n", src2, src2, dest );

        return EXIT_SUCCESS;
    }

    void dollars( register char *dst, register char const *src ){
        int len;
        
        if( dst == NULL || src == NULL ){
            return;
        } 
        
        *dst++ = '$';
        len = strlen( src );
        /*
        ** 如果数字字符的长度足够长,复制将出现在小数点左边的数字,并在适当的位置添
        ** 加逗号。如果字符串短于3个数字,在小数点前面再添加一个'0' 
        */
        if( len >= 3 ){
            int i;
            
            for( i = len - 2; i > 0; ){
                *dst++ = *src++;
                if( --i > 0 && i % 3 == 0 ){
                    *dst++ = ',';
                }
            }
        } else{
            *dst++ = '0';
        }
        
        /*
        ** 存储小数字,然后存储'src'中剩余的数字。如果'src'中的数字少于2个数
        ** 字,用'0'填充,然后在'dst'中添加NUL终止符。 
        */
        *dst++ = '.';
        *dst++ = len < 2 ? '0' : *src++;
        *dst++ = len < 1 ? '0' : *src;
        *dst = 0; 
    }
    /* 输出:

    */ 
    16. 这个程序与前一个练习的程序相似,但它更为通用。它按照一个指定的格式字符串对一个数字字符串进行格式化,类似于许多BASIC编译器所提供的的“print using”语句。函数的原型应该如下:
        int format( char *format_string, char const *digit_string );
    digit_string中的数字根据一开始在format_string中找到的字符从右到左逐个复制到format_string中。注意,被修改后的format_string依然是以NUL字节结尾的。根据格式化过程中是否出现错误,函数返回真或假。
    格式字符串可以包含下列字符。
    # 在两个字符串中都是从右向左进行操作。格式字符串的每个#字符都被数字字符串中的下一个数字取代。如果数字字符串用完,格式字符串中所有剩余的#字符由空白代替(但存在例外,请参见下面对小数点的讨论)。
    , 如果逗号左边至少有一个数字,那么它就不做修改,否则它由空白代替。
    . 小数点始终作为小数点存在。如果小数点左边没有一位数字,那么小数点左边的那个位置以及右边直到有效数字位置的所有位置都用0填充。
    下面的例子说明了调用这个函数的一些结果。符号*用于表示空白。
    为了简化这个项目,可以假定格式字符串所提供的的格式总是正确的。最左边至少有一个#符号,小数点和逗号的右边也至少有一个#符号,而且逗号绝不会出现在小数点的右边。我们需要进行检查的错误只有:
    a) 数字字符串中的数字多于格式字符串中的#符号;
    b) 数字字符串为空。
    发生这两种错误时,函数返回假,否则返回真。如果数字字符串为空,格式字符串在返回时应未做修改。如果使用指针而不是下标来解决问题,将会学到更多的东西。
            格式字符串            数字字符串            结果格式字符串
            #####                  12345                  12345
            #####                  123                      123
            ##,###                 1234                    1,234
            ##,###                 123                      123 
            ##,###                 1234567               34,567
            #,###,###.##       123456789           1,234,567.89
            #,###,###.##       1234567               12,345.67
            #,###,###.##       123                       1.89
            #,###,###.##       1                            0.01
            #####.#####       1                            0.00001 
    提示:开始时让两个指针分别指向格式字符串和数字字符串的末尾,然后从右向左进行处理。对于作为参数传递给
    函数的指针,必须保留它的值,这样就可以判断是否到达了这些字符串的左端。 

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    int format( char *format_string, char const *digit_string );

    int main( void ){
        char format_string[] = "##,###";
        char digit_string[] = "1234567";
        
        printf( "original format_string = %s\ndigit_string = %s\n", format_string, digit_string );
        format( format_string, digit_string );

        return EXIT_SUCCESS;
    }

    int format( char *format_string, char const *digit_string ){
        /*
        ** discuss digit_string is a null string literal.
        */
        if( *digit_string == '\0' ){
            return 0;
        }
        
        size_t format_size;
        size_t digit_size;
        
        format_size = strlen( format_string );
        digit_size = strlen( digit_string );
        /* pf is the behind of the end of format_string, pd is the behind of the end of digit_string */
        char *pf = format_string + format_size;
        const char *pd = digit_string + digit_size;
        /* record the location of format_string when the pd arrives to the first location of digit_string. */
        char *pfe; 
        /* judge the correctness of result */
        int flag = 1;
        /* record the number of '#' in the format_string */
        int counter = 0;
        
        /*
        ** format digit string.
        */
        while( pf != format_string ){
            --pf;
            if( *pf == '#' ){
                counter++;
                /*
                ** digit_string doesn't be used up, and *pf is equal to '#'.
                */
                if( pd != digit_string ){
                    pfe = pf;
                    --pd;
                    *pf = *pd;    
                }
                else { /* digit_string is used up. */
                    /*
                    ** assume that * is equal to ' '.
                    */
                    *pf = '*';
                }
            }  else if( *pf == ',' ){
                /*
                ** In the condition of digit_string does't be used up,
                ** If *pf is equal to ',', *pf uses ',' to fill format_string. 
                ** Otherwise, '*' replaces ',' to fill format_string instead of ' '.
                */
                if( pd != digit_string ){
                    pfe = pf;
                    *pf = ',';
                } else{
                    *pf = '*';
                }
            } else if( *pf == '.' ){
                /*
                ** if *pf is equal to '.', *pf uses '.' to fill format_string.
                ** If digit_string doesn't be used up,
                ** the front character of '.' in the format_string uses the character of digit_string 
                ** to fill format_string. Otherwise, the front character of '.' uses '0' to fill format_string 
                ** and the latter character uses '0' to fill format_string until pft is equal to pfe 
                ** instead of ' '.
                */
                *pf = '.';
                if( pd != digit_string ){
                    --pf;
                    --pd;
                    *pf = *pd; 
                } else{
                    for( char *pft = pf + 1; pft < pfe; ++pft ){
                        *pft = '0';
                    }
                    --pf;
                    *pf = '0';
                }
            }
        }
        if( counter < digit_size ){
            flag = 0;
        }
        printf( "format_string = %s, flag = %d\n", format_string, flag );
        if( flag == 1 ){
            return 1;
        } else {
            return 0;
        }}
    /* 输出:

    */ 

    /* 答应我遇到困难坚决不要放弃!!!你要相信光,盖亚 */

    17. 这个程序与前两个练习类似,但更加一般化。它允许调用程序把逗号放在大数的内部、去除多余的前导零以及提供一个浮动美元符号等。
    这个函数的操作类似于IBM370机器上的Edit和Mark指令。它的原型如下:
        char *edit(char *pattern, char const *digits );
    它的基本思路很简单。模式(pattern)就是一个图样,处理结果看上去应该像它的样子。数字字符串的字符根据这个图样所提供的方式从左向右复制到模式字符串。数字字符串的第1位有效数字很重要。结果字符串中所有在第一位有效数字之前的字符都由一个“填充”字符代替,函数将返回一个指针,它所指向的位置正是第1位有效数字在结果字符串中的存储位置(调用程序可以根据这个返回指针,把一个浮动美元符号放在这个值左边的毗邻位置)。这个函数的输出结果就像支票上打印的结果一样---这个值左边所有的空白由星号或其他字符填充。
    在描述这个函数的详细处理过程之前,看一些这个操作的例子是很有帮助的。为了清晰起见,符号*用于表示空格。结果字符串中带下划线的那个数字就是返回值指针所指向的字符(也就是第1位有效数字),如果结果字符串中不存在带下划线的字符,就说明函数的返回值是个NULL指针。 
                模式字符串        数字字符串        结果字符串
                *#,###            1234            *1,234
                *#,###            123456        *1,234
                *#,###            12                *1,2 
                *#,###            0012            ****12
                *#,###                12            ****12
                *#,###              1                ***100
                *X#Y#Z               空           **
                 #,##!.##           23456       ***234.56
                 #,##!.##         023456       ***234.56
                 #,##!.##               456       

    $4.56 
                 #,##!.##         0    6           
    $0.06
                 #,##!.##         0                 $$$
                 #,##!.##         1                 $1,
                 #,##!.##         Hi there      $H,i0t.he
    现在,让我们来讨论这个函数的细节。函数的第1个参数就是模式,模式字符串的第1个字符就是“填充字符”。函数使数字字符串
    修改模式字符串中剩余的字符来产生结果字符串。再吹过程中,模式字符串将被修改。输出字符串不可能比原先的模式字符串更长,
    所以不能存在溢出第1个参数的危险(因此不需要对此进行检查)。
    模式是从左向右逐个字符进行处理的。每个位于填充字符后面的字符处理结果将是三选一:原样保留,不做修改;被一个数字字符串
    中的字符代替;被填充字符代替。
    数字字符串也是从左向右进行处理的,但它本身在处理过程中绝不会修改。虽然它被称为“数字字符串”,但是也可以保留任何其他
    字符,如上面的例子所示。但是,数字字符串中的空格应该和数字0一样对待(它们的处理结果相同)。
    函数必须保持一个“有效”标志,用于标志是否有任何有效数字从数字字符串复制到模式字符串。数字字符串中的前导零并非有效
    数字,其余的字符都是有效数字。
    如果模式字符串或数字字符串有一个是NULL,那就是个错误。在这种情况下,函数应该立即返回NULL。
    下标列出了所有需要的处理过程。列标题“signif”就是有效标志。“模式”和“数字”分别表示模式字符串和数字字符串的下一个
    字符。表的左边列出了所有可能出现的不同情况,表的右边描述了每种情况需要的处理过程。例如,如果下一个模式字符是'#',有效
    标志就设为“假”。数字字符串的下一个字符是'\0',所以用一个填充字符代替模式字符串中的#字符,对有效标志不做修改。
                            如果你找到这个......                        你应该这样处理......    
        模式            signif            数字            模式        signif        说明
        '\0'             无关紧要        不使用            不做修改    不做修改    返回保存的指针
        '#'                无关紧要        '\0'            '\0'        不做修改    返回保存的指针
                        假                '0'或' '        填充字符    不做修改     
                                        其他任何字符    数字        真            保存指向该字符的指针
                        真                任何字符        数字        不做修改
        '!'                无关紧要        '\0'            '\0'        不做修改    返回保存的指针
                        假                任何字符        数字        真            保存指向该字符的指针
                        真                任何字符        数字        不做修改    
        其他任何符号     假                不使用            填充字符    不做修改 
                        真                不使用            不做修改    不做修改 
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char *edit(char *pattern, char const *digits );

    int main( void ){
        char pattern[] = "*#,##!.##";
        char const *digits = "Hi there"; 
        printf( "original pattern = %s, digits = %s\n", pattern, digits );
        edit( pattern, digits );
        printf( "after edit( pattern, \"%s\" ), pattern = %s\n", digits, pattern );

        return EXIT_SUCCESS;
    }

    char *edit(char *pattern, char const *digits ){
        /* special condition */
        if( digits == NULL || pattern == NULL ){
            return NULL;
        }
        size_t pattern_len;
        pattern_len = strlen( pattern );
        char *ppe = pattern + pattern_len;
        /* store original pattern pointer */
        char *ori_pat = pattern;
        /* record the result of function */
        char *retd = NULL;
        /* the location of begin of loop */
        char *beg_pattern = pattern + 1;
        /* 
        ** record true or false of leading space or leading zero.
        ** If flag is equal to zero, represents it is a valid number,
        ** If flag is equal to one, represents it is a invalid number
        ** (leading space or leading zero).  
        */
        int flag = 1;
        
        while( beg_pattern <= ppe ){
            if( *beg_pattern == '\0' ){
                printf( "1\n" ); 
                return retd;
            } else if( *beg_pattern == '#' ){
                if( *digits == '\0' ){
                    *beg_pattern = '\0';
                    ++digits;
                    printf( "2\n" );
                    return retd;
                }  
                if( flag == 1 ){
                    if( *digits == '0' || *digits == ' ' ){
                        *beg_pattern = *ori_pat;
                        ++digits;
                    } else{
                        *beg_pattern = *digits;
                        retd = beg_pattern;
                        flag = 0;
                        ++digits;
                        printf( "*beg_pattern = %c, flag = %d\n", *beg_pattern, flag ); 
                    }
                } else{
                    if( *digits == ' ' ){
                        *beg_pattern = '0';
                    } else{
                        *beg_pattern = *digits; 
                    }
                    ++digits;
                }                 
            } else if( *beg_pattern == '!' ){
                if( flag == 1 || flag == 0 ){
                    if( *digits == '\0' ){
                        *beg_pattern = '\0';
                        ++digits;
                        printf( "3\n" );
                        return retd;
                    }
                } 
                if( flag == 1 ){
                    if( *digits == ' ' ){
                        *beg_pattern = '0';
                    } else{
                        *beg_pattern = *digits;
                    }
                    flag = 0;
                    ++digits;
                    retd = beg_pattern;
                } else{
                    if( *digits == ' ' ){
                        *beg_pattern = '0';
                    } else{
                        *beg_pattern = *digits;
                    }
                    ++digits;
                }
            } else{
                if( flag == 1 ){
                    /* printf("+"); */
                    *beg_pattern = *ori_pat;
                } 
            }
            ++beg_pattern;            
        }
    }
    /* 输出:

    */

    /* 醉卧沙场君莫笑 古来征战几人回。坚决不能放弃,要矢志不渝 */

    /* 我都测试过了,答案跟题目给的答案啊一模一样,如果你发现其他题有什么问题,可以在评论区评论,可以一起结果问题的。*/

  • 相关阅读:
    SpringCloud Bus消息总线
    神经网络常用的训练方式,人工神经网络训练过程
    剑指offer 16 数值的整数次方
    String类常用方法
    跳房子 I
    WAS项目更新单个文件
    网站优化搜索引擎与关键词
    金仓数据库 KingbaseES 插件DBMS_UTILITY
    PDF格式分析(八十六)——修订注释(Redaction)
    UE5报错及解决办法
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/125393149