LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 983|回复: 6

怎么解决字符输入的一些问题?

[复制链接]
发表于 2006-3-20 11:05:14 | 显示全部楼层 |阅读模式
在程序中用到函数scanf,getchar,读取输入的一个字符时,如果参数是字符型的,在读取单个字符时,往往会出现意外的情况。例如

scanf("%c",&a);
while(c!='q'){
printf("again\n");
scanf("%c",&a);
}

当程序运行的时候,第一次输入一个字符,假设为b,则程序的输出为
again
again
也就是输出了两次循环那的语句。
调试发现,在第一此进入循环的时候,c的值确实是scanf读取的那个字符b。但是当第一次执行到循环内的scanf的时候,程序将不再执行这条语句,而是当作a已经被复制,于是跳过scanf,进入到下一轮的循环当中。这就是为什么会输出两个again的原因。
在对单个字符进行读取的时候,常常不注意就会出现这样的问题。请问大家这是什么原因,有什么方法可以避免?
发表于 2006-3-22 08:58:31 | 显示全部楼层
那是因为回车也算是一个字符,不知道下面这个处理方式是否符合你的要求:

  1. [rick@Fedora-Core test]$ cat test.c
  2. #include <stdio.h>

  3. int main()
  4. {
  5.         char c[2] = {0};
  6.         scanf("%s",&c);
  7.         while(c[0]!='q' || c[1]!=0)
  8.         {
  9.                 fprintf(stderr,"again\n");
  10.                 scanf("%s",&c);
  11.         }
  12.         return 0;
  13. }
复制代码


不过会有缓冲区溢出的危险
回复 支持 反对

使用道具 举报

发表于 2006-3-22 16:03:05 | 显示全部楼层
为什么不会溢出???
这样也许可以解决你的问题:

scanf("%c",&a);
fflush(stdin);
回复 支持 反对

使用道具 举报

发表于 2006-3-22 17:46:49 | 显示全部楼层
把终端设置成为 Non-Canonical 模式。
Canonical versus Non-Canonical Modes
The two problems are closely related. By default, terminal input is not made available to a program until
the user presses Enter. In most cases, this is a benefit because it allows the user to correct typing mistakes
using Backspace or Delete. Only when they’re happy with what they see on the screen do they press
Enter to make the input available to the program.
This behavior is called canonical, or standard, mode. All the input is processed in terms of lines. Until a
line of input is complete (usually when the user presses Enter), the terminal interface manages all the
key presses, including Backspace, and no characters may be read by the application.
173
Terminals
The opposite of this is non-canonical mode, where the application has much greater control over the processing
of input characters. We’ll come back to these two modes again a little later.
Among other things, the Linux terminal handler likes translating interrupt characters to signals and can
automatically perform Backspace and Delete processing for you, so you don’t have to reimplement it in
each program you write. We’ll find out more about signals in Chapter 11.
So, what’s happening in our program? Well, Linux is saving the input until the user presses Enter, then
passing both the choice character and the subsequent Enter to the program. So, each time you enter a menu
choice, the program calls getchar, processes the character, then calls getchar again, which immediately
returns with the Enter character.
The character the program actually sees isn’t an ASCII carriage return, CR (decimal 13, hex 0D), but a line
feed, LF (decimal 10, hex 0A). This is because, internally, Linux (like UNIX) always uses a line feed to
end lines of text; that is, UNIX uses a line feed alone to mean a newline, where other systems, such as
MS-DOS, use a carriage return and a line feed together as a pair. If the input or output device also sends
or requires a carriage return, the Linux terminal processing takes care of it. This might seem a little
strange if you’re used to MS-DOS or other environments, but one of the very considerable benefits is
that there is no real difference between text and binary files on Linux. Only when you input or output
to a terminal or some printers and plotters are carriage returns processed.
We can correct the major deficiency in our menu routine simply by ignoring the additional line feed
character with some code such as this:
do {
selected = getchar();
} while(selected == ‘\n’);
This solves the immediate problem. We’ll return to the second problem of needing to press Enter, and a
more elegant solution to the line feed handling later.
回复 支持 反对

使用道具 举报

发表于 2006-3-23 01:10:20 | 显示全部楼层
我也是C语言初学者,刚开始也很不习惯C语言处理字符的方式,现在已经有一点心得了。

解决这个问题的办法是用一个类似

while((c = getchar()) != '\n')
  ;

这样的循环把包括回车键在内的全部字符“读”完,一个不漏。然后再慢慢处理。这个技巧在k&r的The C Programming Language的第一章里使用得很多。

下面针对楼主的具体情况给出我的解决方案,在debian里用GCC编译通过。如果用户输入了一个不是 q 的字符,或者输入了两个或更多的字符,又或者不输入字符直接按了回车键,程序都会提示 again 要求用户重新输入,并给出相应的错误代码。


  1. #include <stdio.h>

  2. int onechar(void);

  3. main()
  4. {
  5.   int c;

  6.   while((c = onechar()) != 'q')
  7.     printf("again (ERORR:%d)\n", c);

  8.   printf("You've enter the charater '%c'.\n", c);

  9.   return 0;
  10. }

  11. int onechar(void)
  12. {
  13.   char a, c;
  14.   int i;

  15.   for (i = 0; (c = getchar()) != '\n'; i++)
  16.     if (i == 0)
  17.       a = c;

  18.   if (i        > 1)
  19.     return 0;
  20.   else  
  21.     return a;
  22. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2006-3-23 11:57:41 | 显示全部楼层
对onechar()函数作了改进。


  1. int onechar(void)
  2. {
  3.   char a, c;

  4.   if ((a = getchar()) == '\n')       //直接按回车
  5.     return a;
  6.   else if ((c = getchar()) == '\n')  //一个字符加回车
  7.     return a;
  8.   else {                             //两个或更多字符加回车
  9.     while(getchar() != '\n')
  10.       ;
  11.     return 0;
  12.   }
  13. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2006-3-23 12:39:43 | 显示全部楼层
Post by mousse
为什么不会溢出???
这样也许可以解决你的问题:

scanf("%c",&a);
fflush(stdin);

c 语言标准里fflush stdin是未定义的
至少glibc里是不能达到预想的目的
有关这个问题
可以使用fgets
如果你的程序只在有glibc的系统里运行,可以用getline
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表