虽然这是一个非常基本的问题,应该很容易解决,但我还是无法弄明白。我试图读取一个字符串,其中包含由stdin的空格分隔的文件名。虽然它似乎正确读取所有文件名,但当我在文件名上使用stat()时,它总是在最后一个文件上失败。这些文件都存在,并且它总是在最后一个文件中失败,例如,如果stdin是“file.txt thisFile.txt thisFile.txt”,如果我要求它将输入打印到stdout,它将打印“file.txt thisFile。 txt thisFile.txt“,但是当我在文件上使用stat()时,它将适用于除最后一个文件之外的所有文件。我相信它与我在文件中阅读的方式有关,任何帮助都表示赞赏。附:如果我用空格“file.txt thisFile.txt thisFile.txt”结束输出,它将统计所有文件没有问题。但是由于程序的其他部分和其他文件需要通过管道来处理,额外的空格不是输入的有效格式。
char *input = 0;
char buffer[10];
size_t curMax = 0;
size_t curLen = 0;
fprintf(stderr, "accessed waiting for input\n");
while(fgets(buffer,sizeof(buffer),stdin)!=0)
{
size_t bufLen = strlen(buffer);
if(curLen+bufLen+1>curMax)
{
size_t newLen = curMax*2+1;
if(bufLen+1>newLen)
newLen=bufLen+1;
char *extra = realloc(input, newLen);
if (!extra)
break;
input = extra;
curMax = newLen;
}
strcpy(input+curLen,buffer);
curLen+=bufLen;
}
fprintf(stderr, "accessed input received: %s\n", input);
int i = 0;
int count = 1;
for (i = 0; i < strlen(input); i++){
if (input[i] == ' ')
count++;
}
char * fileNames[count];
char * pch = strtok(input, " \0FEOFfeof\n");
int j = 0;
for (j = 0; pch != NULL; j++){ //Read file names
fileNames[j] = (char *)malloc(strlen(pch));
strncpy(fileNames[j],pch, strlen(pch));
pch = strtok(NULL, " \0FEOFfeof\n");
}
答案 0 :(得分:3)
接受回答后
"\0"
中的" \0FEOFfeof\n"
(八进制转义序列)导致截断模式
char * pch = strtok(input, " \0FEOFfeof\n");
// same as
char * pch = strtok(input, " ");
推荐一些
的变体char * pch = strtok(input, " \n\r\t\v\f");
答案 1 :(得分:2)
fgets()
将'\n'
留在读取字符串的末尾。
所以你只需要删除它,一个例子就是
while (fgets(buffer, sizeof(buffer), stdin) != NULL)
{
size_t length
length = strlen(buffer);
if (buffer[length - 1] == '\n')
buffer[length - 1] = '\0';
...
}
答案 2 :(得分:1)
您的代码存在许多问题,这是注释版本:
char *input = 0;
为清楚起见,您应该使用NULL
代替0
来获取空指针。
char buffer[10];
size_t curMax = 0;
size_t curLen = 0;
fprintf(stderr, "accessed waiting for input\n");
while(fgets(buffer,sizeof(buffer),stdin)!=0)
与上述相同,!= NULL
代替!=0
,或者您可以删除
完全测试并写下:while (fgets(buffer, sizeof(buffer), stdin))
,这是C中公认的习语。
{
size_t bufLen = strlen(buffer);
if(curLen+bufLen+1>curMax)
{
size_t newLen = curMax*2+1;
if(bufLen+1>newLen)
newLen=bufLen+1;
在某些情况下,测试无法分配足够的内存:如果是第一个
line有1个字节,分配2个字节的缓冲区并设置curMax
2
。如果第二行有4个字节,则input
将被重新分配给
5个字节,1个字节太短,无法容纳1 + 4 + 1个字节的内容加上尾随'\0'
。
您应该将测试更改为if(curLen+bufLen+1>newLen) newLen=curLen+bufLen+1;
char *extra = realloc(input, newLen);
if (!extra)
break;
input = extra;
curMax = newLen;
}
strcpy(input+curLen,buffer);
curLen+=bufLen;
}
fprintf(stderr, "accessed input received: %s\n", input);
int i = 0;
int count = 1;
for (i = 0; i < strlen(input); i++){
不要为循环的每次迭代重新计算strlen(输入)!不要 依赖于编译器的聪明才能优化这段代码,在很多情况下,它 不会。
if (input[i] == ' ')
count++;
}
char * fileNames[count];
char * pch = strtok(input, " \0FEOFfeof\n");
正如chux指出的那样,模式中的嵌入式\0
实际上结束了它。
此外,您无法以这种方式测试EOF
。 EOF
是一个特殊值
由getc
返回,表示未能从中读取字符
stream,is不是可以存储在字符串中的实际字符。
如果您更改模式以处理其他间距字符,您
还必须更改计算文件名数量的循环
相应
int j = 0;
for (j = 0; pch != NULL; j++){ //Read file names
fileNames[j] = (char *)malloc(strlen(pch));
完全分配strlen(pch)
是一个经典的错误。你需要分配
在字符串末尾存储'\0'
的至少一个额外字节。如果
您的系统上可以使用strdup
,它可以满足您的需求。一世
不明白为什么这个有用的BSD功能从未进入过
标准。
strncpy(fileNames[j],pch, strlen(pch));
请勿使用 strncpy
。这是非常容易出错的,不会对你做什么
期望。在这种情况下,memcpy
会做你的意思,复制
没有结尾'\0'
的文件名。但我怀疑这不是你的意思
真的意味着。您可以使用fileNames[j] = strdup(pch);
pch = strtok(NULL, " \0FEOFfeof\n");
}
strtok
也容易出错,因为它使用静态隐藏变量来保存
上下文。如果你在调用之间做了更复杂的事情
strtok
,此上下文可能会被覆盖,您的代码将展示
奇怪的行为,你可能会在这样的错误上失去很长时间。使用
strtok_r
或strsep
,如果您的系统可用。
关于strdup
:如果它不可用,请自行定义:
#include <stdlib.h>
#include <string.h>
char *strdup(const char *ptr) {
char *newptr;
if ((newptr = malloc(strlen(ptr) + 1)) != NULL) {
strcpy(newptr, ptr);
}
return newptr;
}