使用 EOF 结束循环(不输入)

Ending a Loop with EOF (without enter)

我目前正在尝试用这样的方式结束 while 循环:

#include <stdio.h>
int main() 
{
    while(getchar() != EOF)
    {
        if( getchar() == EOF )
            break;
    }
    return 0;

}

当我在 Ubuntu 上按 CTRL+D 时,它会立即结束循环。但是在 Windows 上我必须按 CTRL+Z 然后按 ENTER 来关闭循环。我可以删除 Windows 上的 ENTER 吗?

getchar 行为

对于linux,EOF字符是用ctrl + d写的,而在Windows上是通过 ctrl + z[=64= 更改 CRT 库的内部状态后,当您按 enter 时由控制台写入](保留此行为是为了与非常旧的系统兼容)。如果我没记错的话,它被称为文件的软结束。我不认为你可以绕过它,因为当你按下 enter 时,EOF char 实际上被你的 getchar 消耗了,而不是当你按下 ctrl[ 时=64=] + z.

据报道here

In Microsoft's DOS and Windows (and in CP/M and many DEC operating systems), reading from the terminal will never produce an EOF. Instead, programs recognize that the source is a terminal (or other "character device") and interpret a given reserved character or sequence as an end-of-file indicator; most commonly this is an ASCII Control-Z, code 26. Some MS-DOS programs, including parts of the Microsoft MS-DOS shell (COMMAND.COM) and operating-system utility programs (such as EDLIN), treat a Control-Z in a text file as marking the end of meaningful data, and/or append a Control-Z to the end when writing a text file. This was done for two reasons:

  • Backward compatibility with CP/M. The CP/M file system only recorded the lengths of files in multiples of 128-byte "records", so by convention a Control-Z character was used to mark the end of meaningful data if it ended in the middle of a record. The MS-DOS filesystem has always recorded the exact byte-length of files, so this was never necessary on MS-DOS.

  • It allows programs to use the same code to read input from both a terminal and a text file.

还报告了其他信息here:

Some modern text file formats (e.g. CSV-1203[6]) still recommend a trailing EOF character to be appended as the last character in the file. However, typing Control+Z does not embed an EOF character into a file in either MS-DOS or Microsoft Windows, nor do the APIs of those systems use the character to denote the actual end of a file.

Some programming languages (e.g. Visual Basic) will not read past a "soft" EOF when using the built-in text file reading primitives (INPUT, LINE INPUT etc.), and alternate methods must be adopted, e.g. opening the file in binary mode or using the File System Object to progress beyond it.

Character 26 was used to mark "End of file" even if the ASCII calls it Substitute, and has other characters for this.

如果您这样修改代码:

#include <stdio.h>

int main() {
  while(1) {
    char c = getchar();
    printf("%d\n", c); 
    if (c == EOF)      // tried with also -1 and 26
      break;
  }
  return 0;
}

然后你测试它,在 Windows 上你会看到 EOF (-1) 在你按下 enter 之前它不会写在控制台中. Beore of that a ^Z 是由终端仿真器打印的(我怀疑)。根据我的测试,如果出现以下情况,则会重复此行为:

  • 您使用 Microsoft 编译器进行编译
  • 你使用 GCC 编译
  • 你运行CMD里的编译代码window
  • 你运行编译代码在bash模拟器在windows

使用 Windows 控制台更新 API

按照@eryksun 的建议,我成功地为 Windows 编写了一个(复杂得离谱)代码,它改变了 conhost 的行为以实际获得 "exit when pressing ctrl + d"。它并没有处理所有事情,它只是一个例子恕我直言,这是要尽可能避免的事情,因为可移植性小于 0。此外,要真正正确处理其他输入情况,需要 更多 应该写代码,因为这些东西会从控制台分离标准输入,你必须自己处理它。

这些方法的工作原理大致如下:

  • 获取标准输入的当前处理程序
  • 创建一个输入记录数组,该结构包含有关主机中发生的事情的信息window(键盘、鼠标、调整大小等)
  • read what happens in the window (it can handle the number of events)
  • 遍历事件向量以处理键盘事件并拦截所需的 EOF(即 4,根据我的测试)退出,或打印任何其他 ascii 字符。

这是代码:

#include <windows.h>
#include <stdio.h>

#define Kev input_buffer[i].Event.KeyEvent // a shortcut

int main(void) {
  HANDLE h_std_in;                // Handler for the stdin
  DWORD read_count,               // number of events intercepted by ReadConsoleInput
        i;                        // iterator
  INPUT_RECORD input_buffer[128]; // Vector of events

  h_std_in = GetStdHandle( // Get the stdin handler
    STD_INPUT_HANDLE       // enumerator for stdin. Others exist for stdout and stderr
  ); 

  while(1) {
    ReadConsoleInput( // Read the input from the handler
      h_std_in,       // our handler 
      input_buffer,   // the vector in which events will be saved
      128,            // the dimension of the vector
      &read_count);   // the number of events captured and saved (always < 128 in this case)

    for (i = 0; i < read_count; i++) {    // and here we iterate from 0 to read_count
      switch(input_buffer[i].EventType) { // let's check the type of event 
        case KEY_EVENT:                   // to intercept the keyboard ones
          if (Kev.bKeyDown) {             // and refine only on key pressed (avoid a second event for key released)
            // Intercepts CTRL + D
            if (Kev.uChar.AsciiChar != 4)
              printf("%c", Kev.uChar.AsciiChar);
            else
              return 0;
          }
          break;
        default:
          break;
      }
    }
  }

  return 0;
}
    while(getchar() != EOF)
    {
        if( getchar() == EOF )
            break;
    }
    return 0;

这里不一致。

如果getchar() != EOF则进入循环,否则(ifgetchar() == EOF)不进入循环。因此,没有理由在循环内检查 getchar() == EOF

另一方面,您调用 getchar() 2 次,您等待输入 2 个字符而不是仅输入 1 个。

你试图做什么?