关键词 getchar() 换行符
前段时间,做线性表子系统实验(代码见附录)。使用了getchar()函数,输入字符。在函数体内,设置变量n记录表长,当输出n值时发现,总是比实际的字符个数多一个。经过反复的测试,初步判断,问题是在getchar()函数上。从网上得到getchar()函数的资料如下:
“getchar 由宏实现:#define getchar() getc(stdin)。getchar有一个int型的返回值.当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中.直到用户按回车为止(回车字符也放在缓冲区中).当用户键入回车之后,getchar才开始从stdin流中每次读入一个字符.getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕.如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取.也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键. ”
从上面的一段话分析,有一个问题:回车是不是也作为一个字符赋值给x?如果是这样的话,n的值就与回车有关了。又通过资料的发现,x=getchar(),如果x为char类型时,很可能出现错误。
“ getchar()的返回值一般情况下是字符,但也可能是负值,即返回EOF。
这里要强调的一点就是,getchar函数通常返回终端所输入的字符,这些字符系统中对应的ASCII值都是非负的。因此,很多时候,我们会写这样的两行代码:
char c;
c = getchar();
这样就很有可能出现问题。因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不正确的。为了能够让所定义的变量能够包含getchar函数返回的所有可能的值,正确的定义方法如下(K&R C中特别提到了这个问题):
int c;
c = getchar(); ”
我有通过,把字符转换成ASCII值的方式输出,发现有一个值是10,这个不是我输入的某个字符,而是LF(line feed)。
所以假设getchar()函数没有问题,也就是x=getchar()
在没有出错误情况下,重点研究这个意外出现的字符---换行符。
搜索资料如下:
回车符号和换行符号产生背景:
关于“回车”(carriage return)和“换行”(line feed)这两个概念的来历和区别。
在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。
这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。
后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。
Unix 系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“ <回车><换行>”,即“\r\n”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
具体区别:
先看个例子:
先生成一个换行(\n, 0x0A)和回车(\r, 0x0D)组合的文本
$ echo -en '12\n34\r56\n\r78\r\n' > tmp
以十六进制方式查看文本
$ od -t x1 tmp
0000000 31 32 0a 33 34 0d 35 36 0a 0d 37 38 0d 0a
0000016
文本文件的行结束符,传统上
PC机 用 CRLF
苹果机用CR
unix 用 LF
CR -- 回车符,c语言'\r'
LF -- 换行符, c语言'\n'
不同计算机上c语言统一规定为:
文本文件的行结束符一律变成一个符号LF,也就是换行符,也就是new line符, 也就是'\n'.
“回车和换行符转换成一个换行符”
对PC机而言,文本文件行结束符,CRLF读入后,丢掉CR,留 LF.
例如fgets() 读入一行,行尾只有LF,没有CR.
在解析文本或其他格式的文件内容时,常常要碰到判定回车换行的地方,这个时候就要注意既要判定"\r\n"又要判定"\n"。写程序时可能得到一行,将其进行trim掉'\r',这样能得到你所需要的string了。'\n' 10 换行(newline)'\r' 13 回车(return)
从上面的资料中,看到c语言是有规定的。这可以解释,为什么系统输出中出现LF。
又有一个问题,就是回车键在结束时,输入的。Getchar()
读入字符不是应该按顺序建立链表。为什么LF首先被读入,建立第一个节点(可以通过调试发现,过程见附录)。
提出假设
(1)getchar()具有某种特点,可能从最后一个字符读入,
(2)回车,换行符的优先级比较高。当回车进入内存后,xoa(LF)最先读入。然后,一次读入其余的字符。
有以前的c知识,可以知道(1)是不存在的。那么看看(2),
网上实例:
#include<stdio.h>
int main(void)
{
int ch1=getchar();
int ch2=getchar();
int ch3=getchar();
printf("%d %d %d ",ch1,ch2,ch3);
int ch4=getchar();
int ch5=getchar();
int ch6=getchar();
printf("%d %d %d ",ch4,ch5,ch6);
}
运行测试分析:
(1)输入123456回车,打印出49 50 51 52 53 54
(2)输入12345回车,打印出49 50 51 52 53 10(10为'\n'的ASCII码)
(3)输入123回车,打印出49 50 51 ,程序执行到 int ch5=getchar()等待输入。再输入456回车,打印出10 52 53
(4)输入1回车,程序执行到 int ch3=getchar()等待输入。再输入23456回车,打印出49 10 50 51 52 53
通过运行,结果是正确的,应该具有一般性。现在,看看我分析(3)中出现10,考察输入的变量情况。ch5接收得是'\n'.
结合(4)可以得出,LF并不具优先级的特性,优先级是对操作符而言的,LF只是一个字符。
也就是说,x的输入情况,与(3)很相似。
重新考察我的建表函数:
void CREATLISTR( )
{ datatype x='q'; //datatype 是char 类型
…………
x=getchar(); // 读入第1个结点的值
while(x!='x') // ‘x’为输入结束符
{ …………………………
n++;// 表长加1
x=getchar();
}
猜想
刚开始的赋值,使getchar()运行了一遍,如同(3)的情况,内存中有一个换行符了。也就是赋值这个语句在进入内存时,和getchar()读入数据很相似,或者有某种联系。
现在修改:
Getchar()在第一次接收字符以前,只有下面的部分输入了字符。在scanf()后加一个getchar(),接收一个换行符。
………………
scanf("%d",&option);
getchar();
执行的结果,没有换行符了。
------问题解决。
那么赋值过程会不会产生一个换行符?通过一些实例验证,
赋值过程,没有换行符的。
void CREATLISTR( )
{ datatype x='q';
// 逐个插入字符,以 ‘x’ 为 结束符
node *s, *r;
head=NULL; // 链表开始为空
r=NULL; // 尾指针初值为空
printf("请输入链表内容:\n");
x=getchar(); // 读入第1个结点的值
while(x!='x') // ‘x’为输入结束符
{
s=(node *)malloc(sizeof(node)); // 生成新结点
s->data=x; // 将x 的值存入新结点的数据域中
if (head==NULL)
head=s;
else
r->next=s;
r=s; // r 始终指向链表的尾结点
n++;// 表长加1
x=getchar();
//putchar(x);
}
if(r!=NULL)
r->next=NULL;
// 保证尾结点的链域为空
}
部分资料参考:
http://www.cnblogs.com/yunf/archive/2011/04/20/2021830.html
http://blog.sina.com.cn/s/blog_5dd8fece0100rwc4.html
http://topic.csdn.net/t/20040617/23/3101951.html
http://blog.sina.com.cn/s/blog_590be5290100kaeg.html