我正在开发一个程序来分隔字符串中的单词,然后在不同的行上打印每个单词。我输出有些困难。对于前者
"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;
}
答案 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)
产生正确输出的代码的两个小变体。
#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'
的程序员进行侮辱。使用NULL
或0
作为空指针;专门用字符'\0'
。
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个字符),主要是因为变量名很长。使用src
和tok
(以及0表示NULL)会将其减少到80个字符以下而不会严重损害可读性。
两种变体都标记输入行:
"this is a string"
进入输出:
"this"
"is"
"a"
"string"