1

我正在开发一个程序,该程序将字符串中的单词分开,然后将每个单词打印在不同的行上。我在输出时遇到了一些困难。例如。

"This is a string"

印刷

""this 
"is"  
"a" 
"string" 
"

代替

"this"
"is"
"a" 
"string"

代码:

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

void wholestring(char S[]) {
   int i;
   for (i=0; i<strlen(S); ++i) {
      }
   return;
}

int main(){
    const int mysize = 100;
    char mystr[mysize];
    char *newstr;

    fgets(mystr, mysize, stdin);
    wholestring(mystr);

    newstr = strtok (mystr, " ");

    while (newstr != '\0'){
        printf ("\"%s\" \n", newstr);
        newstr = strtok ('\0', " ");
    }
    return 0;
}
4

4 回答 4

3

Xagyg 关于从源字符串中删除引号是正确的;只需从中删除引号。发生的情况是起始引号与第一个单词组合在一起,因为它在第一个空格之前,因此您从字符串中获得“,从 printf 中获得”。你在“字符串”上有同样的问题。字符串之后的第一个“”是您读入的文件中的“。然后,您从读取的字符串中获取换行符,它将光标向下移动到下一行,然后您从 printf 中获取“。

要解决这个问题,您既需要从原始字符串中删除引号,也需要在源文件末尾没有换行符(只能处理单行源文件,因为 fgets 需要换行符来分隔行)或在您这样读取字符串后从字符串中删除任何换行符:

int mystr_length = strlen(mystr);
for (int i = mystr_length-1; i >= 0 && (mystr[i] == '\r' || mystr[i] == '\n'); i--) {
    mystr[i]='\0';
}

此外,newstr 是一个指针,因此您应该将它与 NULL 指针“NULL”而不是 NUL ascii 字符“\0”进行比较。与 strtok 的第一个参数相同。它想要 NULL 而不是 NUL。

您是否有可能在字符串末尾有一个空格来获得最后一个 strtok 响应?

这就是您所做的,而不是从文件中读取字符串,而是从硬编码到您的程序中的字符串开始。

此外,在开始解析之前打印出您读取的字符串,这样您就知道它看起来像您想要的那样。

最后,您的“whole_string”函数有一个大问题。对于字符串中的每个字符,它都在重新计算字符串的长度,这要求它查看字符串中的每个字符。对于非常长的字符串,这可能需要很长时间。相反,在循环之前将长度缓存到变量中并使用它

int string_length = strlen(S);
for (i=0; i<string_length; ++i) {

此外,正如这个问题中所述,该函数实际上并没有做任何事情,除非通过字符串真的低效地什么都不做。

我曾经把它放在顶部,但这都是错误的:你正在捕获换行数据然后打印它。它打印一个引号,光标被发送到同一行的开头,然后另一个引号打印在它的顶部,这就是为什么你在最后一行只看到一个。

于 2013-05-19T05:30:01.443 回答
3

我在您的代码中看到了两个问题

newstr = strtok (mystr, " ");

如果您的输入在引号内,例如“这是一个字符串”,这将有问题

代替

这是一个字符串

""this  <-- Here
"is"  
"a" 
"string" 
"

是因为引号内的输入“”

这可以清除

newstr = strtok (mystr, "\"| ");

另一个是缓冲区末尾的下一行字符需要用NULL清除,否则你一定会得到exitra换行符或引号,如here

""this

"is"

"a"

"string"

" <-- Here 

抱歉回答不完整

fgets(mystr, mysize, stdin);
wholestring(mystr);

/** ensure Next line is no more available **/
 if(mystr[strlen(mystr)-1] == '\n')
     mystr[strlen(mystr)-1] = '\0';

    newstr = strtok (mystr, "\"| ");
于 2013-05-19T06:22:26.337 回答
1

产生正确输出的代码的两个次要变体。

变体 A

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

int main(void)
{
    char line[4096];

    if (fgets(line, sizeof(line), stdin) != 0)
    {
        static const char delims[] = " \"\n";
        char *token = strtok(line, delims);

        while (token != NULL)
        {
            printf("\"%s\"\n", token);
            token = strtok(NULL, delims);
        }
    }
    return 0;
}

这避免了使用可变长度数组。在 C 中,const int mysize = 100;andchar mystr[mysize];创建一个 VLA 因为mysize不是编译时常量表达式。C++ 将创建一个常规数组。差异在很大程度上无关紧要,但它确实告诉我您正在使用 C99 编译器(或 C++ 编译器)。

但是,常数确实没有任何好处mysize。你应该sizeof(mystr)在对 的调用中使用fgets(),然后mysize只被引用一次,所以它也可以用一个常量替换——我习惯性地使用 4096 作为单行输入,因为除了书签文件之外的任何东西都不太可能是一条比那更长的单行。

使用变量delims意味着字符串不重复;如果分隔符更改,则只需更改一行。

我还重命名了变量;'my' 前缀在我看来总是像 'baby talk' 并且从未出现在我的代码中。

请注意,代码在调用fgets(). 养成检查每个输入函数的返回状态的习惯永远不会太早!在检查输出函数时,我和其他人一样懒惰printf(),但输入函数真的很重要。

我还消除了输出行的尾随空白;这些真的让我很恼火——就像任何地方的代码中的尾随空格一样。

另请注意,虽然'\0'它是一个空指针常量,但它不是一种传统的编写方式,并且会导致(轻微的)混乱,然后对滥用'\0'. 使用NULLor0表示空指针;专门'\0'用于字符。

变体 B

变体 A 代码中有明显的重复;该函数有两次调用strtok(),但我们可以编写代码,这样只有一次调用,导致:

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

int main(void)
{
    char line[4096];

    if (fgets(line, sizeof(line), stdin) != 0)
    {
        char *token;
        for (char *source = line; (token = strtok(source, " \n\"")) != NULL; source = NULL)
            printf("\"%s\"\n", token);
    }
    return 0;
}

由于现在只有一个调用strtok(),因此只有一个对分隔符的引用,因此它们可以再次成为文字字符串。循环行for有点长(91 个字符),主要是因为变量名很长。使用srcand tok(和 0 表示 NULL)可以将其减少到 80 个字符以下,而不会严重损害可读性。

两种变体都对输入行进行标记:

"this is a string"

进入输出:

"this"
"is"
"a"
"string"
于 2013-05-19T16:23:47.973 回答
1

mystr在开始使用标记器 ( strtok)之前删除第一个和最后一个双引号。或者在不带引号的情况下标记它的副本。

于 2013-05-19T05:55:47.963 回答