[讨论] 俺的C习题(6)——这是一个相对复杂一些的字符串处理函数

辛昕   2012-1-9 22:40 楼主
其实昨晚就开始修改。
发现了很多当初没有留意的bug。还对一些子函数做了修改。
不过至今,对于最后一个去掉重复词项的代码块还没有完全理解。拖了两天,先贴上来,静待圈哥批判.....

还发现了一个奇怪的字符串长度的奇怪问题。
具体问题都写在 注释里。

  1. /*practise 8.4*/
    /*题目:
    定义一个函数,其参数是一个字符串,返回该字符串中的单词数。
    单词以空格或标点符号来分隔,假设字符串不含单双引号,也就是说不存在像isn't这样的词。
    定义第二个函数,它的第一个参数是一个字符串,第二个参数是一个数组,该函数将第一个字符串变元分割成单词,把这些单词存储在第二个数组变元中,最后返回这些单词。
    第三个函数,其参数是一个字符串,返回该字符串中的字母数。
    使用这些函数实现一个程序,从键盘读入含有文本的字符串,输出文本中的所有单词,输出顺序按照单词字母数由短到长。
    */
    /*
    关于 isalpha()
    */
    #include<stdio.h>
    #include<stdlib.h>
    #include<ctype.h>
    #include<string.h>

    #define increment 10
    #define WORDLEN 10

    int account(char *pstring); //统计词数
    void get_words(char pstring[],char* pwords[]); //拆分成词项,存入pwords[]
    int Get_length(char str[]);
    char** arrangement(char* pwords[],int);


    int main(void)
    {
    int capacity = 20;
    int count = 0;
    char *pstring = NULL;
    char *temp = NULL;
    int words = 0;
    char **pwords = NULL;
    char **buffer = NULL;
    int position = 0;
    int number = 0;

    pstring = (char *)malloc(capacity * sizeof(char));
    printf("Enter a string:\n");

    do
    {
    if(count == capacity)
    {
    capacity += increment;
    temp = (char *)malloc(capacity * sizeof(char));

    if(!temp)
    {
    printf("Allocation failed.\n");
    return -1;
    }

    if(!pstring)
    {
    for(int i = 0;i < count;i++)
    *(temp + i) = *(pstring + i);
    pstring = temp;
    free(temp);
    }
    }
    }while((*(pstring + count++) = getchar()) != '\n');
    /*结束字符串*/

    *(pstring + count) = 0;


    /*统计词数*/
    words = account(pstring);

    /*根据词数分配数组容纳词数*/
    pwords = (char **)malloc(words * sizeof(char *));

    /*为每个词分配空间*/
    for(int n = 0; n < words;n++)
    pwords[n] = (char*)malloc(WORDLEN*sizeof(char));//当年的恶习,这个小写,我半天没看出原来是个宏
    //看来这个正确版本和那个错误版本都是我写的,不是参考答案

    get_words(pstring,pwords);

    pwords = arrangement(pwords,words);

    /*这一部分属于一开始写程序时,一个阶段一个阶段检查已有结果时所用*/
    /*结果最后忘了关闭测试。另外是,number先赋值为1,而在其后输出的时候*/
    /*又用的先使用再加1,这种做法不好,所以我选择初始化为0,然后先加加再使用*/
    for(number = 0;number < words;number++)
    printf("%s\n",pwords[number]);

    // number = 1;

    /*从题目出发,这一部分是没用的*/
    /*但我忘记了打问号那一句的作用和意义*/

    buffer = (char **)malloc(words * sizeof(char *));
    buffer[0] = pwords[0];//?????

    for(int k = 1;k < words;k++)
    {
    for(int j= 0;j <= position;j++)
    {
    if(!strcmp(buffer[j],pwords[k]))
    break;
    if(j == position && strcmp(buffer[j],pwords[k]))
    buffer[++position] = pwords[k];
    }
    }

    /*看到这一部分,才会想起这是用来剔除重复的单词,所以上面的那个输出是用来测试是否读取正确的*/
    /*但上述代码并未实现该功能*/
    for(int l = 0;l < position;l++)
    printf("%s\n",buffer[l]);

    free(pwords);
    pwords = NULL;
    free(buffer);
    buffer = NULL;
    return 0;
    }

    int account(char *pstring)
    {
    int count = 0;
    char flag = 0;
    int i = 0;
    /*
    for(unsigned int i = 0; i < strlen(pstring);i++) //过去我真的是干了太多这种事情
    //过去,我一直以为是C99和ansi的区别,但刚刚看的C和指针,
    //即应该把定义放在所有语句之前。
    //我怀疑我是不是弄错了?
    {
    if(!isalpha(*(pstring + i)) && isalpha(*(pstring + i + 1)) )
    count++;
    }
    if(isalpha(*(pstring)) )//????
    count++;
    */
    /*这种计数方法,使得这个程序弱不禁风*/
    /*这样的输入就能让它报废:a big big, (space)de */
    /*合理的方法应该是按顺序遍历整个字符串,逢字母开始坐标志,隔了一个以上的非字母,再碰到字母,计数*/

    /*
    充分考虑几种可能的输入情况:
    " I am a big big girl"
    "I am a big big girl, in the big big world"
    "I am a big big girl."
    */

    while(*(pstring + i) != '\0')
    {
    if(isalpha(*(pstring + i++)))
    {
    if(!flag)
    flag = 1;
    }
    else
    {
    if(flag)
    {
    count++;
    flag = 0;
    }
    }

    }
    /*下面这一段居然是画蛇添足?!!*/
    //if(isalpha(*(pstring+i)) && !flag)
    // count++;
    return count;
    }

    void get_words(char pstring[],char* pwords[])
    {
    int count = 0;
    int i = -1; //这里是为了配合后边的“先加再用”,让i从零开始,但是,赋值为-1是否有什么危险呢?
    int j = 0;
    /*
    while(pstring[i] != '\0')
    {
    if(!isalpha(pstring[i]))
    {
    i++;
    continue;
    }
    j = 0;
    while(isalpha(pstring[i]))
    pwords[count][j++] = pstring[i++];
    pwords[count++][j] = '\0';
    }
    */

    /*原来的写法很蠢,其实可以while,不用if*/
    /*改完了,花了半个小时修正,发现其实没进步多少,还是要两个循环变量*/

    while(pstring[++i] != '\0')
    {

    if(isalpha(pstring[i]))
    pwords[count][j++] = pstring[i];
    else if(j/*strlen(pwords[count])*/)
    {
    pwords[count++][j] = '\0';
    j = 0;
    }
    //printf("length of %d is %d\n",count,strlen(pwords[count]));
    /*这里有个很奇怪的问题,为什么pwords[count]的长度总是17?????*/
    }
    }

    int Get_length(char str[])
    {
    int length = 0;
    while(str[length] != '\0')
    length++;
    return length;
    }

    char** arrangement(char* pwords[],int words)
    {
    char* temp = NULL;
    temp = (char *)malloc(WORDLEN * sizeof(char));
    //char temp[WORDLEN];

    for(int i = 0;i < words;i++)
    {
    for(int j = 0;j < words;j++)
    {
    if(Get_length(pwords[i]) < Get_length(pwords[j]))
    {
    temp = pwords[j];
    pwords[j] = pwords[i];
    pwords[i] = temp;
    }
    }
    //free(temp);// = NULL;
    }
    //free(temp);
    return pwords;
    }

强者为尊,弱者,死无葬身之地

回复评论 (3)

顶,不过没仔细看
点赞  2012-1-10 09:19

辛昕这个程序着实不短,这次我采用直接在原程序中加注的办法来挑刺,不然的话,实在不好对照


/*practise 8.4*/
/*题目:
定义一个函数,其参数是一个字符串,返回该字符串中的单词数。
单词以空格或标点符号来分隔,假设字符串不含单双引号,也就是说不存在像isn't这样的词。
定义第二个函数,它的第一个参数是一个字符串,第二个参数是一个数组,该函数将第一个字符串变元分割成单词,把这些单词存储在第二个数组变元中,最后返回这些单词。
第三个函数,其参数是一个字符串,返回该字符串中的字母数。
使用这些函数实现一个程序,从键盘读入含有文本的字符串,输出文本中的所有单词,输出顺序按照单词字母数由短到长。
*/
/*
关于 isalpha()
*/
#include
#include
#include
#include

#define increment 10
#define WORDLEN 10

int account(char *pstring); //统计词数
void get_words(char pstring[],char* pwords[]); //拆分成词项,存入pwords[]
int Get_length(char str[]);
char** arrangement(char* pwords[],int);


int main(void)
{
int capacity = 20;
int count = 0;
char *pstring = NULL;
char *temp = NULL;
int words = 0;
char **pwords = NULL;
char **buffer = NULL;
int position = 0;
int number = 0;

pstring = (char *)malloc(capacity * sizeof(char));
printf(\"Enter a string:\n\");

do
{
if(count == capacity)
{
capacity += increment;
temp = (char *)malloc(capacity * sizeof(char));

if(!temp)
{
printf(\"Allocation failed.\n\");
return -1;
}

if(!pstring)
{
for(int i = 0;i < count;i++)
*(temp + i) = *(pstring + i);
pstring = temp;
free(temp);
}
}
}while((*(pstring + count++) = getchar()) != '\n');
/*结束字符串*/

*(pstring + count) = 0;


/*统计词数*/
words = account(pstring);

/*根据词数分配数组容纳词数*/
pwords = (char **)malloc(words * sizeof(char *));

/*为每个词分配空间*/
for(int n = 0; n < words;n++)
pwords[n] = (char*)malloc(WORDLEN*sizeof(char));//当年的恶习,这个小写,我半天没看出原来是个宏
//看来这个正确版本和那个错误版本都是我写的,不是参考答案

get_words(pstring,pwords);

pwords = arrangement(pwords,words);//圈注,这个地方也有点危险,象pwords这种由malloc赋值的指针,在free之前赋值都是隐患,尽管在这个地方是自己赋值给了自己
//圈注,稳妥的写法还是 arrangement(pwords,words);
/*这一部分属于一开始写程序时,一个阶段一个阶段检查已有结果时所用*/
/*结果最后忘了关闭测试。另外是,number先赋值为1,而在其后输出的时候*/
/*又用的先使用再加1,这种做法不好,所以我选择初始化为0,然后先加加再使用*/
for(number = 0;number < words;number++)
printf(\"%s\n\",pwords[number]);

// number = 1;

/*从题目出发,这一部分是没用的*/
/*但我忘记了打问号那一句的作用和意义*/

buffer = (char **)malloc(words * sizeof(char *));
buffer[0] = pwords[0];//?????

for(int k = 1;k < words;k++)
{
for(int j= 0;j <= position;j++)
{
if(!strcmp(buffer[j],pwords[k]))
break;
if(j == position && strcmp(buffer[j],pwords[k]))
buffer[++position] = pwords[k];
}
}

/*看到这一部分,才会想起这是用来剔除重复的单词,所以上面的那个输出是用来测试是否读取正确的*/
/*但上述代码并未实现该功能*/
for(int l = 0;l < position;l++)
printf(\"%s\n\",buffer[l]);

free(pwords);
pwords = NULL;
free(buffer);
buffer = NULL;
return 0;
}

int account(char *pstring)
{
int count = 0;
char flag = 0;
int i = 0;
/*
for(unsigned int i = 0; i < strlen(pstring);i++) //过去我真的是干了太多这种事
//圈注:象上边写的 i < strlen(pstring) ;从语法来看尽管没错,但是每判一次循环条件,就要调用一次 strlen(pstring)函数,

//大大增加了运行时的负担(尤其是对实时,嵌入式系统而言
// 正确的方法是,将strlen(pstring)先赋值给某个临时变量,或者改为,for(i=strlen(pstring)-1;i>=0;)

//过去,我一直以为是99和ansi的区别,但刚刚看的C和指针,
//即应该把定义放在所有语句之前。
//我怀疑我是不是弄错了?
{
if(!isalpha(*(pstring + i)) && isalpha(*(pstring + i + 1)) )
count++;
}
if(isalpha(*(pstring)) )//????
count++;
*/
/*这种计数方法,使得这个程序弱不禁风*/
/*这样的输入就能让它报废:a big big, (space)de */
/*合理的方法应该是按顺序遍历整个字符串,逢字母开始坐标志,隔了一个以上的非字母,再碰到字母,计数*/

/*
充分考虑几种可能的输入情况:
\" I am a big big girl\"
\"I am a big big girl, in the big big world\"
\"I am a big big girl.\"
*/

while(*(pstring + i) != '\0')
{
if(isalpha(*(pstring + i++)))
{
if(!flag)
flag = 1;
}
else
{
if(flag)
{
count++;
flag = 0;
}
}
}
//圈注,上边这段话明显有问题,具体分析我就不写了,下边改为正确的写法,对比一下.

while(*(pstring + i) != '\0')
{
if(isalpha(*(pstring + i++)))
{
if(!flag)
{
flag = 1;
count++;
}
}
else
flag = 0;
}
//圈注,上边的flag=的1和0最好定义成一个有名字意义的宏,这样不容易搞错.



/*下面这一段居然是画蛇添足?!!*/
//if(isalpha(*(pstring+i)) && !flag)
// count++;
return count;
}

void get_words(char pstring[],char* pwords[])
{
int count = 0;
int i = -1; //这里是为了配合后边的“先加再用”,让i从零开始,但是,赋值为-1是否有什么危险呢?
int j = 0;
/*
while(pstring != '\0')
{
if(!isalpha(pstring))
{
i++;
continue;
}
j = 0;
while(isalpha(pstring))
pwords[count][j++] = pstring[i++];
pwords[count++][j] = '\0';
}
*/

/*原来的写法很蠢,其实可以while,不用if*/
/*改完了,花了半个小时修正,发现其实没进步多少,还是要两个循环变量*/

while(pstring[++i] != '\0')
{

if(isalpha(pstring))
pwords[count][j++] = pstring;
else if(j/*strlen(pwords[count])*/)
{
pwords[count++][j] = '\0';
j = 0;
}
//printf(\"length of %d is %d\n\",count,strlen(pwords[count]));
/*这里有个很奇怪的问题,为什么pwords[count]的长度总是17?????*/
}
}

int Get_length(char str[])
{
int length = 0;
while(str[length] != '\0')
length++;
return length;
}

char** arrangement(char* pwords[],int words)
{
char* temp = NULL;
temp = (char *)malloc(WORDLEN * sizeof(char));

//圈注,上边这个temp只是用来临时交换 pwords[j] 和 pwords的指针变量,,这里却给它分配了WORDLEN字符变量的空间,简直是莫名其妙


//char temp[WORDLEN];

for(int i = 0;i < words;i++)
{
for(int j = 0;j < words;j++)
{
if(Get_length(pwords) < Get_length(pwords[j]))
{
temp = pwords[j];//续前圈注,这里对temp赋值,把malloc分配给它的空间丢了,副作用是产生内存泄露,这也说明对temp分配空间毫无道理
pwords[j] = pwords;
pwords = temp;
}
}
//free(temp);// = NULL;
}
//free(temp);
return pwords;
}

能力越大,责任越大;知道越多,未知更多
点赞  2012-1-10 14:48

回复 板凳 能圈就圈 的帖子


刚刚加班完......明天再一个一个细看哈。
那个count++的地方明白了。
强者为尊,弱者,死无葬身之地
点赞  2012-1-10 23:24
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复