从 stdin c 读取文件名

Reading in filenames from stdin c

虽然这是一个非常基本的问题,应该很容易解决,但我一直没弄明白。我试图读入一个字符串,其中包含由标准输入中的空格分隔的文件名。虽然它似乎正确读取了所有文件名,但当我在文件名上使用 stat() 时,它总是在最后一个文件上失败。所有文件都存在,但最后一个文件总是失败,例如,如果标准输入是 "file.txt thisFile.txt thisFile.txt",如果我要求它将输入打印到标准输出,它将打印 "file.txt thisFile.txt thisFile.txt",但是当我使用 stat( ) 在文件上它将适用于除最后一个文件之外的所有文件。我相信这与我阅读文件的方式有关,我们将不胜感激。 P.S。如果我以空格 "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, " [=10=]FEOFfeof\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, " [=10=]FEOFfeof\n");
}

fgets() 在读取字符串的末尾留下 '\n'

所以你只需要删除它,一个例子是

while (fgets(buffer, sizeof(buffer), stdin) != NULL)
 {
    size_t length
    length = strlen(buffer);
    if (buffer[length - 1] == '\n')
        buffer[length - 1] = '[=10=]';
    ...
 }

接受答案后

" [=13=]FEOFfeof\n" 中的 "[=12=]"(八进制转义序列)导致截断模式

char * pch = strtok(input, " [=10=]FEOFfeof\n");
// same as 
char * pch = strtok(input, " ");

推荐

的一些变体
char * pch = strtok(input, " \n\r\t\v\f");

你的代码有很多问题,这里是注释版本:

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 个字节的内容加上结尾 '[=27=]'。 您应该将测试更改为 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(input)!不要 依靠编译器的聪明才智来优化这段代码,在很多情况下,它 不会的。

    if (input[i] == ' ')
        count++;
}
char * fileNames[count];
char * pch = strtok(input, " [=14=]FEOFfeof\n");

正如 chux 指出的那样,模式中嵌入的 [=29=] 实际上结束了它。 此外,您不能以这种方式测试 EOFEOF 是一个特殊值 getc 返回表示未能从 流,不是可以存储在字符串中的实际字符。 如果您更改模式以处理其他间距字符,您 还必须更改计算文件名数量的循环 因此。

int j = 0;
for (j = 0; pch != NULL; j++){ //Read file names
    fileNames[j] = (char *)malloc(strlen(pch));

精确分配 strlen(pch) 是一个典型的错误。你需要分配在 至少一个额外的字节来存储字符串末尾的 '[=27=]'。如果 strdup 在你的系统上可用,它会做你想做的事。我 不明白为什么这个有用的 BSD 函数从未进入 标准。

    strncpy(fileNames[j],pch, strlen(pch));

请勿使用strncpy。它很容易出错,并且不会做你想做的事 预计。在这种情况下,memcpy 会执行您的意思,复制 没有结尾 '[=27=]' 的文件名。但我怀疑这不是你 真的意思。您可以将这两行替换为 fileNames[j] = strdup(pch);

    pch = strtok(NULL, " [=17=]FEOFfeof\n");
}

strtok 也容易出错,因为它使用静态隐藏变量来保存 上下文。如果你在调用之间做更复杂的事情 strtok,此上下文可能会被覆盖,您的代码将显示 奇怪的行为,你可能会因为这样的错误而浪费很长时间。采用 strtok_rstrsep 如果您的系统可用。

关于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;
}