【问题标题】:Reading From Stdin Twice in C在 C 中两次读取标准输入
【发布时间】:2016-03-01 21:53:15
【问题描述】:
    int getLineCount() {
        int ret = 0;
        char c;
        while ((c = fgetc(stdin)) != EOF)
            if (c == '\n')
                ret++;
        return ret + 1;
    }

    void fill(char *WORD) {
        int charIndex = 0;
        char c;
        while ((c = fgetc(stdin)) != EOF) {
           *(WORD + charIndex++) = c;
        }
    }

    int main() {
        int lineNum = getLineCount();
        char *WORD = (char*)calloc(lineNum * 18,sizeof(int));

        fill(WORD);
        return 0;
    }

这是我的代码的一部分,我的问题是(如您所见):

我试图读取 stdin 的内容两次,但是在 getLineCount 函数之后,它停留在 EOF 并且我无法在 fill 函数中再次读取它。

我在 Linux 中使用此命令从用户那里获取标准输入;

$./output < text_file.txt

有没有办法将标准输入回滚到起始字符?如果没有,我该如何解决这个问题?

谢谢。

【问题讨论】:

  • @user3121023:假设你可以倒带stdin是一种不好的风格。
  • 为什么不存储之前读取的值?这就是变量的用途。
  • 我同意@Olaf 的观点,假设您可以倒带stdin 是不好的。对于那些质疑倒带能力的人......如果它是一个终端,那么你当然不能,但如果频道是一个通常允许倒带的设备,那么你当然可以。问题是您通常无法控制 stdin 是什么设备类型 - 因此假设 rewind()/fseek() 可以在这里完成是不好的做法。
  • @DoğaCanYanıkoğlu 我强烈建议您考虑其他 cmets 而不是 dup 答案..
  • fgetc() 的调用返回的类型是整数,而不是字符。文字EOF 是一个整数(通常值为-1)如果您的编译器将char 设置为无符号,那么char 和EOF 之间的比较将失败。强烈建议更改此:char c; 至此int c;(2 个位置)其他所有内容都不需要更改。

标签: c input buffer stdin stdio


【解决方案1】:

您可以使用rewind(stdin) 将流设置回文件的开头,但请注意,它不能保证正常工作,特别是如果流是管道、终端或设备。

您的分配方案不正确:您可以计算文件的大小,然后分配那么多字节,但是您当前的 (char*)calloc(lineNum * 18,sizeof(int)); 为每一行分配的大小是 int 类型大小的 18 倍。一些具有短行的文件将适合此数组,而其他文件将调用未定义的行为。

请注意,c 必须定义为 intc = fgetc(stdin); 才能正确存储包括 EOF 特殊值在内的所有值。

【讨论】:

  • 只是部分代码(不完整),由于解决方案的要求,必须是18。
  • @DoğaCanYanıkoğlu:18 什么?如果行是固定宽度和 18 字节,包括\n,你应该分配calloc(lineNum * 18,1)
  • 这是一个二维字符串数组。 18 是单词的长度,每个存储单元中有 1 个字符。 @chqrlie
  • @DoğaCanYanıkoğlu:那为什么是sizeof(int)
【解决方案2】:

不要使用rewind

当然,您可以保存从标准输入读取的数据(如果文件对于主内存来说太大,则可能保存在文件中)并对其进行操作。

另一种可能性是这样的:

struct callback {
  void (*call) (char, void *);
  void * data;
};

void with_characters_from(FILE * file, struct callback const * callbacks, size_t count) {
  int c;
  while ((c = fgetc(file)) != EOF) {
    char character = c & 0xFF;
    for (size_t i = 0; i < count; ++i) {
      callbacks[i].call(character, callbacks[i].data);
    }
  }
}

您进行反向控制,这样您的函数不再是“从”标准输入“拉出数据”,而是将数据(字符)“推送到”它们。请注意,这可能导致回调地狱,并且在 C 中,您牺牲了很大一部分类型安全(以及代码清晰度.. 没有一流的函数/闭包...叹息)。

A small test:

struct counter_data {
  char const character;
  unsigned count;
};

void counter (char character, void * vptr) {
  struct counter_data * data = vptr;
  if (character == data->character) {
    ++(data->count);
  }
}

int main() {
  struct counter_data data [2] = {
    {'a', 0}, {'x', 0}};
  struct callback callbacks [2] = {
    {&counter, &(data [0])},
    {&counter, &(data [1])}};
  with_characters_from (stdin, callbacks, 2);
  printf("Counted %c %u times \n", data [0].character, data [0].count);
  printf("Counted %c %u times \n", data [1].character, data [1].count);
  return 0;
}

如前所述,对于您的特定示例,您应该考虑一种完全不同的方法:如果可能,请事先计算所需的大小。如果您超过了该大小(您应该始终对其进行测试),则使用realloc 以获得更大的内存块。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-16
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    • 2020-03-07
    相关资源
    最近更新 更多