【问题标题】:How to read the standard input into string variable until EOF in C?如何将标准输入读入字符串变量,直到 C 中的 EOF?
【发布时间】:2010-03-23 00:13:43
【问题描述】:

我在尝试将 stdin 读入 char* 变量时遇到“总线错误”。 我只想阅读来自stdin 的全部内容并将其首先放入一个变量中,然后继续处理该变量。

我的代码如下:

char* content;
char* c;
while( scanf( "%c", c)) {
 strcat( content, c);
}

fprintf( stdout, "Size: %d", strlen( content));

但不知何故,我总是通过调用 cat test.txt | myapp 返回“总线错误”,其中 myapp 是上面的编译代码。

我的问题是如何在 EOF 之前将 stdin 读入变量?正如您在代码中看到的,我只想打印来自标准输入的输入的大小,在这种情况下它应该等于文件的大小test.txt

我认为只使用scanf 就足够了,也许是缓冲方式来读取stdin

【问题讨论】:

  • 为什么不直接使用 stat() 获取文件大小,然后使用文件大小 + 1 来(尝试) malloc 缓冲区,然后 read() 到缓冲区?

标签: c stdin


【解决方案1】:

既然你不关心实际的内容,为什么还要构建一个字符串呢?我也会使用getchar():

int    c;
size_t s = 0;

while ((c = getchar()) != EOF)
{
  s++;
}

printf("Size: %z\n", s);

此代码将正确处理文件中包含 '\0' 字符的情况。

【讨论】:

  • OP 说:“我只想读取来自标准输入的全部内容并将其首先放入变量中,然后继续处理该变量。”我认为对它的 length() 调用只是一个例子。但是您对文件具有 '\0' 字符的评论很重要——他们将其放在单个变量中的整个想法在那里可能有点缺陷。
  • c 应声明为int 以包括char 加上EOF 的范围。
  • @Brooks,我一定是在阅读时错过了这一点。无论如何,我认为答案是一个合理的例子。
【解决方案2】:

首先,您传递的是未初始化的指针,这意味着scanfstrcat 将写入您不拥有的内存。其次,strcat 需要两个以 null 结尾的字符串,而 c 只是一个字符。这将再次导致它读取您不拥有的内存。您不需要 scanf,因为您没有进行任何实际处理。最后,一次读一个字符是不必要的慢。这是解决方案的开始,对最终字符串使用可调整大小的缓冲区,并为 fgets 调用使用固定缓冲区

#define BUF_SIZE 1024
char buffer[BUF_SIZE];
size_t contentSize = 1; // includes NULL
/* Preallocate space.  We could just allocate one char here, 
but that wouldn't be efficient. */
char *content = malloc(sizeof(char) * BUF_SIZE);
if(content == NULL)
{
    perror("Failed to allocate content");
    exit(1);
}
content[0] = '\0'; // make null-terminated
while(fgets(buffer, BUF_SIZE, stdin))
{
    char *old = content;
    contentSize += strlen(buffer);
    content = realloc(content, contentSize);
    if(content == NULL)
    {
        perror("Failed to reallocate content");
        free(old);
        exit(2);
    }
    strcat(content, buffer);
}

if(ferror(stdin))
{
    free(content);
    perror("Error reading from stdin.");
    exit(3);
}

编辑:正如 Wolfer 所暗示的,输入中的 NULL 将导致字符串在使用 fgets 时过早终止。如果可用,getline 是更好的选择,因为它处理内存分配并且不存在 NUL 输入问题。

【讨论】:

  • 使用 gcc 4.8.1 在 char *content = malloc(sizeof(char) * BUF_SIZE);content = realloc(content, contentSize); 上提供 invalid conversion from ‘void*’ to ‘char*’ [- fpermissive] ...
  • 在标准输入中显示0x00 时也会失败。
  • @Wolfer,gcc 的 C 编译器 has no -fpermissive。您是否将其编译为 C++;那可以解释错误吗?标签说 C。你说得对,fgets 不能很好地处理 NUL 字符。它不会导致任何未定义的行为,但您不会知道并且字符串将被提前终止。我将在问题中添加有关 getline 的注释。
  • 哎呀,我的错,对不起。
【解决方案3】:

假设您想要获取(短于 MAXL-1 字符)字符串而不是逐字符处理文件,我执行以下操作:

#include <stdio.h>
#include <string.h>
#define MAXL 256

main(){
  char s[MAXL];
  s[0]=0;
  scanf("%s",s);
  while(strlen(s)>0){
    printf("Size of %s : %d\n",s,strlen(s));
    s[0]=0;
    scanf("%s",s);
  };
}

【讨论】:

    【解决方案4】:

    你的问题是你从来没有分配过ccontent,所以它们没有指向任何定义的地方——它们很可能指向一些未分配的内存,或者根本不存在的东西。然后你将数据放入其中。您需要先分配它们。 (这就是总线错误的典型含义;您尝试进行无效的内存访问。)

    (或者,由于c 始终只包含一个字符,您可以将其声明为char c 并将&amp;c 传递给scanf。无需声明一串字符即可。)

    一旦你这样做了,你就会遇到确保content 的长度足以容纳所有输入的问题。要么你需要猜测你期望有多少输入并至少分配这么长的时间(如果超过了就会出错),或者如果它不够长,你需要一个策略来重新分配它以更大​​的大小。

    哦,您还会遇到strcat 需要一个字符串而不是单个字符的问题。即使您将c 保留为char*scanf 调用也不会使其成为字符串。单字符串是(在内存中)一个字符后跟一个空字符以指示字符串的结尾。 scanf,在扫描单个字符时,不会在其后放入空字符。结果,strcpy 不会知道字符串的结尾在哪里,而是会在内存中徘徊寻找空字符。

    【讨论】:

    • 它们不是 NULL,它们有未定义的值。
    • @Brooks,只是将c 变成char 并使用&amp;c 是不够的,因为strcat() 需要一个以null 结尾的字符串。
    • Carl:谢谢,我只是在补充。马修,感谢您的评论;我也会编辑添加它。
    【解决方案5】:

    这里的问题是你引用了一个指针变量,没有通过malloc分配内存,因此结果将是未定义的,不仅如此,通过在可能指向任何东西的未定义指针上使用strcat ,您最终遇到了总线错误!

    这将是所需的固定代码....

    char* 内容 = malloc (100 * sizeof(char)); 字符 c; 如果(内容!= NULL){ 内容[0] = '\0'; // 谢谢大卫! 而 ((c = getchar()) != EOF) { 如果(strlen(内容)

    代码突出了程序员管理内存的责任——对于每个malloc,都有一个free,如果没有,你就有内存泄漏!

    编辑:感谢 David Gelhar 指出我的故障!我已经修复了上面的代码以反映修复...当然在现实生活中,也许可以将固定值 100 更改为 #define 以便通过将缓冲区加倍来轻松扩展缓冲区通过realloc 调整内存大小...

    【讨论】:

    • 此代码有一个错误:第一次通过循环时,您对未初始化的数据调用“strlen”。 (当然,如果存在超过 100 个输入字符,您应该检查缓冲区溢出)
    • 正如 Jon Purdy 在其他帖子中指出的那样,您需要将 c 声明为 int,而不是 char,以便可能的 EOF 结果在范围内。否则你会进入一个无限循环。此外,您的 strcat 调用需要两个以 null 结尾的字符串,而不是字符串和单个字符。正如所写,它不会编译。
    猜你喜欢
    • 2010-10-23
    • 2018-04-28
    • 1970-01-01
    • 2014-05-09
    • 2023-03-06
    • 1970-01-01
    • 2014-11-10
    • 2021-08-02
    • 1970-01-01
    相关资源
    最近更新 更多