再发对于EOF的理解,Windows下面ctrl+z的WM_CHAR值为26!

cleaf 2009-07-06 10:31:25
加精
解读如下:
windows对于标准输入中的EOF的处理,实际上是用了一个很不常用的键盘消息,即(ctrl+z)这个组合,当按下这个组合键的时候,会有:WM_KEYDOWN(90),WM_CHAR(26),WM_KEYUP(90),这三个消息。

windows把WM_CHAR这个消息值放到标准输入缓冲区里面去,这时候有两种情况。

第一:
********26**********,也就是在这个ctrl+z对应的键盘WM_CHAR值之前还有值,那么此时getchar读到26的时候不把其认定为EOF,而只是等视为'\n',返回ascii码的26对应的字符(右向箭头),然后忽略后面所有的东西,清空缓冲区,等待下一次的输入。

第二:
26******************,此时缓冲区的第一个值就是26,那么此时认定为EOF,getchar返回EOF。

所以对于下面的输入,有如下结果:

#include<stdio.h>
#include<stdlib.h>

int main()
{
int c;
//characters 0-255
while ((c = getchar())!=EOF)
if(c != '\n')
printf("%d\n",c);
//EOF
printf("End-Of-File\n");
return 0;
}

说白了,系统用了一个很不常用的键盘组合产生的消息值,来代表EOF,然后是否认定为EOF,需要判断出现的位置。
当出想在缓冲区第一个位置的时候,认定为EOF。

不知道这么理解对不对,请版上的大侠看看。
...全文
5193 62 打赏 收藏 转发到动态 举报
写回复
用AI写文章
62 条回复
切换为时间正序
请发表友善的回复…
发表回复
woeser 2012-05-19
  • 打赏
  • 举报
回复
不知道楼主还看此帖不,或者后面看到的兄弟姐妹能否帮忙解答?按楼主的程序,输入abc^zabc\n,发现输出不是:
97
98
99
26
_(即等待下一次输出)
而是:
97
98
99
_(即等待下一次输出)

我想问ctrl+Z是否不能存入到缓冲区内?因为看到过说:Ctrl+Z产生的不是一个普通的ASCII码值,也就是说它产生的不是一个字符,所以不会跟其它从键盘上输入的字符一样能够存放在输入缓冲区。
woeser 2012-03-19
  • 打赏
  • 举报
回复
看的云里雾里,不过好贴还是要顶啊
cjjiwei 2011-03-11
  • 打赏
  • 举报
回复
看了n久,才看到43楼,明天起来接着看!
好贴!!!顶起!顶起!!!
kosl90 2010-12-17
  • 打赏
  • 举报
回复
http://support.microsoft.com/kb/253257/zh-cn
在这个链接里面清楚的写着EOF被定义为CTRL + Z 或 ASCII 26。
我支持楼主的说法。。。
小程 2010-11-24
  • 打赏
  • 举报
回复
getchar调用getc,getc最终调用ReadFile读数据。
getc的输入流与两个句柄相关,一个在顶层,可以用FILE*定义,暂且称为fp句柄,另一个在底层(__pioinfo),暂且称为io句柄。
如果不考虑unicode,回车的处理以及出错情况,而且是纯输入的文本模式,getc的流程可以简单归结如下:

if (缓冲区里有数据)
取走一个字节
else {
if (io句柄已有EOF标志)
0字节
else {
if (ReadFile读回字节数为0)
0字节
else {
for (每个字节)
{
if (CTRLZ) (1)
if (DEV) (2)
存入缓冲区
else
io句柄置EOF标志
跳出
else
存入缓冲区
}
} /* ReadFile读回字节数不为0 */

} /* io句柄没有EOF标志 */

if (0字节) {
fp句柄置EOF标志
return EOF
}

if (io句柄有EOF标志)
fp句柄置_IOCTRLZ标志

取走一个字节
}


说明(1):判断是否为Ctrl-Z标志,值为26。
说明(2):判断是否为设备文件,比如stdin是设备文件,而普通的文本文件不是设备文件。

~Zabc\n
abc~Zdef\n

情况1:getc从stdin读(即getchar),输入为"~Zabc\n"
在这种情况下,ReadFile会读回0字节。io句柄不会置EOF标志,fp句柄会置EOF标志。getc返回-1,"~Zabc\n"都被丢弃,重新输入后,还可正常工作。

情况2:getc从stdin读(即getchar),输入为"abc~Zdef\n"
getc返回'a'。由于是设备文件,所以io句柄不会置EOF标志。把~Z当做一个普通字符放入缓冲区,但后续的"def\n"被丢弃。fp不会置EOF标志。重新输入后,可以正常工作。

情况3:getc以文本方式从普通文件读,文件内容为"~Zabc...."
ReadFile可以正常读回数据,但检测到~Z时,io句柄会置EOF标志并跳出,所以缓冲区里还没有数据。fp句柄置EOF标志,getc返回-1,后续的调用都返回-1,即整个文件被丢弃。

情况4:getc以文本方式从普通文件读,文件内容为"abc~Zdef...."
ReadFile可以正常读回数据,但检测到~Z时,io句柄会置EOF标志并跳出,缓冲区里有3个字节的数据。getc返回'a'。在获取完"bc"之后,后续的调用将返回-1,并使fp句柄置上EOF标志,后面的文件内容都被丢弃。
小程 2010-11-24
  • 打赏
  • 举报
回复
getchar调用getc,getc最终调用ReadFile读数据。
getc的输入流与两个句柄相关,一个在顶层,可以用FILE*定义,暂且称为fp句柄,另一个在底层(__pioinfo),暂且称为io句柄。
如果不考虑unicode,回车的处理以及出错情况,而且是纯输入的文本模式,getc的流程可以简单归结如下:

if (缓冲区里有数据)
取走一个字节
else {
if (io句柄已有EOF标志)
0字节
else {
if (ReadFile读回字节数为0)
0字节
else {
for (每个字节)
{
if (CTRLZ) (1)
if (DEV) (2)
存入缓冲区
else
io句柄置EOF标志
跳出
else
存入缓冲区
}
} /* ReadFile读回字节数不为0 */

} /* io句柄没有EOF标志 */

if (0字节) {
fp句柄置EOF标志
return EOF
}

if (io句柄有EOF标志)
fp句柄置_IOCTRLZ标志

取走一个字节
}

说明(1):判断是否为Ctrl-Z标志,值为26。
说明(2):判断是否为设备文件,比如stdin是设备文件,而普通的文本文件不是设备文件。

~Zabc\n
abc~Zdef\n

情况1:getc从stdin读(即getchar),输入为“~Zabc\n”
在这种情况下,ReadFile会读回0字节。io句柄不会置EOF标志,fp句柄会置EOF标志。getc返回-1,“~Zabc\n”都被丢弃,重新输入后,还可正常工作。

情况2:getc从stdin读(即getchar),输入为“abc~Zdef\n”
getc返回'a'。由于是设备文件,所以io句柄不会置EOF标志。把~Z当做一个普通字符放入缓冲区,但后续的"def\n"被丢弃。fp不会置EOF标志。重新输入后,可以正常工作。

情况3:getc以文本方式从普通文件读,文件内容为“~Zabc....”
ReadFile可以正常读回数据,但检测到~Z时,io句柄会置EOF标志并跳出,所以缓冲区里还没有数据。fp句柄置EOF标志,getc返回-1,后续的调用都返回-1,即整个文件被丢弃。

情况4:getc以文本方式从普通文件读,文件内容为“abc~Zdef....”
ReadFile可以正常读回数据,但检测到~Z时,io句柄会置EOF标志并跳出,缓冲区里有3个字节的数据。getc返回'a'。在获取完"bc"之后,后续的调用将返回-1,并使fp句柄置上EOF标志,后面的文件内容都被丢弃。
mymtom 2010-05-22
  • 打赏
  • 举报
回复
围观
回复内容太短了!
mymtom 2010-05-22
  • 打赏
  • 举报
回复
EOF就是EOF,表示遇到流结束。
与Ctrl+Z没有任何关系(Unix通常用Ctrl+D)
无论是Ctrl+Z还是Ctrl+D,只是当输入流为终端设备时,系统用来产生EOF的特殊字符。
但是0x1A(Ctrl+Z)和0x04(Ctrl+D)同样是可以通过标准输入读到的。
在Unix上只需输入先输入Ctrl+V这样就可以从键盘输入0x1A和0x04了。
在Unix上这些都是有终端规程来处理的。实际上我是通过网络联入Unix系统的。
实际上EOF与键盘本身根本就没有特定的关系,
在Unix上很容易验证。下面的代码在
SunOS t1000 5.10 Generic_118833-33 sun4v sparc SUNW,Sun-Fire-T1000 Solaris
运行有如下的结果

#include <stdio.h>

int
main(int argc, char *argv[])
{
int ch;

for (;;)
while ((ch = getchar()) != EOF)
printf("%02x\n", ch);

return 0;
}

/*
$ ./hello
^Z 这里的直接^Z, 在Unix下是让进程停止
[1]+ Stopped ./b002
$ fg fg命令将刚才停止的进程放到前台继续运行
./hello
^ZA 先输入^V再输入^Z然后输入A并回车
1a
41
0a
^DA 先输入^V再输入^D然后输入A并回车
04
41
0a
^D 直接输入^D然后输入A并回车
0a
^C 先输入^V再输入^C并回车
03
0a
^C 直接输入^C进程收到SIGINT信号,退出了。
$ uname -a
SunOS t1000 5.10 Generic_118833-33 sun4v sparc SUNW,Sun-Fire-T1000 Solaris
*/


cdlmagical 2010-05-03
  • 打赏
  • 举报
回复
火速围观
2010-03-05
  • 打赏
  • 举报
回复
晕,这老帖怎么又被顶起来了……
lovesi3344 2010-03-05
  • 打赏
  • 举报
回复
还没结贴啊
shiweifu 2010-02-04
  • 打赏
  • 举报
回复
MARK
yuqilin811 2010-01-10
  • 打赏
  • 举报
回复
找了好久 终于有人说出我的心声了 顶!!
TideverMem 2009-09-14
  • 打赏
  • 举报
回复
刚刚看到这个帖子,为什么不继续讨论了?

好像还是没有结论吧。
cleaf 2009-08-31
  • 打赏
  • 举报
回复
其实我看了半天,发现你的口气是最大的,那你给我详细解释一下,你说的二进制想表达什么问题吧,放心我计算机组成原理和汇编都学过,操作系统也学过,当然都是学校学习模式,很粗糙,所以水品很低,不过应该听的懂你说的二进制什么意思。
我主要想听听怎么样才是系统实际干的事情,不需要具体,模型就可以。
[Quote=引用 11 楼 taodm 的回复:]
哎,人家好不容易自圆其说了,何必再搅混呢。
反正只要能自圆其说的解释,也没啥危害。

[/Quote]
cleaf 2009-08-31
  • 打赏
  • 举报
回复
我当时在水木问,其实发现这个东西挺蹩脚,因为我没有系统学过windows操作系统,不知道系统怎么处理。
不过发现其他人也都是一知半解。。。
这东西挺别扭的,尤其是windows搞得东西。
[Quote=引用 43 楼 rouyao 的回复:]
引用 42 楼 lpf000 的回复:
26******************,此时缓冲区的第一个值就是26,那么此时认定为EOF,getchar返回EOF。
]

个人认为你的表述不准确,或许没有仔细通读整个主题帖。

通读整个主题贴后,个人理解,请指正,谢谢:

不是把0x1A(26)认定为EOF(因为输入流"is an ordered sequence of characters",
都是character,所以0x1A也是字符,它在Windows下的输出表现即为楼主所说的“右向箭头”)

而是碰到0x1A后,有两种情况:
1、如果它前面已有字符,那么不认为流结束了,但是忽略了它后面的字符,等待继续输入。
2、如果它前面没有字符,那么就认为流结束了,也就忽略了它后面的字符。
  此时,操作系统和标准输入输出库就安排程序接收EOF值(EOF为在 <stdio.h>中定义的字符常量,值为 -1),
  getchar返回EOF的值,while执行结束。
[/Quote]
cleaf 2009-08-31
  • 打赏
  • 举报
回复
你能不能具体说说操作系统,比如windows如何设置流的状态?
不是说很具体的那种,基本模型就可以。
[Quote=引用 1 楼 arong1234 的回复:]
这不对,他并不好在缓冲区内部存入特定的字符,他直接把流的状态表示为结束,而不是在里面插入特殊字符序列

[/Quote]
rouYao 2009-07-08
  • 打赏
  • 举报
回复
[Quote=引用 42 楼 lpf000 的回复:]
26******************,此时缓冲区的第一个值就是26,那么此时认定为EOF,getchar返回EOF。
[/Quote]]

个人认为你的表述不准确,或许没有仔细通读整个主题帖。

通读整个主题贴后,个人理解,请指正,谢谢:

不是把0x1A(26)认定为EOF(因为输入流"is an ordered sequence of characters",
都是character,所以0x1A也是字符,它在Windows下的输出表现即为楼主所说的“右向箭头”)

而是碰到0x1A后,有两种情况:
1、如果它前面已有字符,那么不认为流结束了,但是忽略了它后面的字符,等待继续输入。
2、如果它前面没有字符,那么就认为流结束了,也就忽略了它后面的字符。
此时,操作系统和标准输入输出库就安排程序接收EOF值(EOF为在 <stdio.h>中定义的字符常量,值为 -1),
getchar返回EOF的值,while执行结束。
lpf000 2009-07-08
  • 打赏
  • 举报
回复
26******************,此时缓冲区的第一个值就是26,那么此时认定为EOF,getchar返回EOF。

同意楼主的说法,我输入EOF的时候,
必须先按下回车,然后再CTRL+Z再回车 才能结束 !=EOF的条件
副组长 2009-07-08
  • 打赏
  • 举报
回复
void CRSGView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
}
无非是键盘扫描码和组合键的状态,^Z就是Z键的扫描码和ctrl,大小写都是OS翻译的,字符也是OS翻译的,OS遇到ctrl+z就翻译成0x1a,就像遇到回车键就翻译成0xd(13) 一样,看着不赶劲有时还加上一个0xa(10),至于进不进缓冲区那更是看OS高不高兴啦。
我觉得这个问题不用讨论,他怎么做的你就怎么用好了。就像微软的for(int i=0; i<..; i++),这个i究竟是循环里面的还是循环外面的?微软已经改过 n 次了。这就像我们在争论一头猪究竟是公猪还是母猪的时候,它突然开口说话了,说:我是一名程序员。 :)
加载更多回复(40)

70,034

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧