字符串格式化,strtok问题

时间:2013-05-19 05:22:38

标签: c string strtok

我正在开发一个程序来分隔字符串中的单词,然后在不同的行上打印每个单词。我输出有些困难。对于前者

"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 个答案:

答案 0 :(得分:3)

Xagyg关于从源字符串中删除引号是正确的;只需删除它的引号。发生的事情是起始引号与第一个单词分组,因为它在第一个空格之前,所以你得到了&#34;从字符串和&#34;来自printf。您在&#34; string&#34;上遇到同样的问题。第一个&#34;在字符串之后是&#34;从你读入的文件中获取。然后,你从你读到的字符串中获取换行符,将光标向下移动到下一行,然后你得到&#34;来自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指针进行比较&#34; NULL&#34;不是NUL ascii字符&#34; \ 0&#34;。与strtok的第一个参数相同。它想要NULL而不是NUL。

你的字符串末尾是否有空格可以让你获得最后的响应?

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

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

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

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

此外,正如这个问题所述,该功能实际上并没有做任何事情,除非通过字符串无效地做任何事情。

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

答案 1 :(得分:3)

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

newstr = strtok (mystr, " ");
如果您的输入在引号内,例如像,那么这将有问题 “这是一个字符串”

而不是

这是一个字符串

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

是因为引号“”

中的输入

可以用

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

另一个是缓冲区末尾的下一行字符需要 用NULL清除,否则你必须获得exitra换行符或引号 就像在这里

""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, "\"| ");

答案 2 :(得分:1)

在开始使用tokenizer(mystr)之前,删除strtok的第一个和最后一个双引号。或者在没有封闭引号的情况下对其副本进行标记。

答案 3 :(得分: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;char mystr[mysize];创建VLA,因为mysize不是编译时常量表达式。 C ++会创建一个常规数组。差异在很大程度上是无关紧要的,但它确实告诉我你正在使用C99编译器(或C ++编译器)。

虽然常数mysize没有任何好处;您应该在sizeof(mystr)的调用中使用fgets(),然后mysize仅被引用一次,因此它也可以替换为常量 - 我习惯性地使用4096作为单个输入行,因为除书签文件之外的任何内容都不太可能是一条比这长的单行。

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

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

请注意,代码在调用fgets()时正确处理了EOF或其他I / O错误。养成检查每个输入功能的返回状态的习惯永远不会太早!在查看printf()等输出函数时,我和其他人一样懒,但输入函数真的很重要。

我也消除了输出线上的尾随空白;那些真的让我烦恼 - 就像代码中的尾随空白一样。

另请注意,尽管'\0'是一个空指针常量,但它不是一种常规的写入方式,并且会导致(轻微)混淆,然后对误用'\0'的程序员进行侮辱。使用NULL0作为空指针;专门用字符'\0'

变体B

Variant 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个字符),主要是因为变量名很长。使用srctok(以及0表示NULL)会将其减少到80个字符以下而不会严重损害可读性。

两种变体都标记输入行:

"this is a string"

进入输出:

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