不按回车就接受一个字符输入!

本文探讨了C语言中字符输入的多种方式,包括getchar()、getch()、getche()及kbhit()函数的使用,并介绍了如何在不按回车键的情况下接收字符输入的方法。
                                                     不按回车就接受一个字符输入!
     记得前几天的一篇日志( http://blog.youkuaiyun.com/anyue417/archive/2006/08/14/1064145.aspx)中写到,像下面这样的程序问题:
代码如下:

#include <stdio.h>

void main()
{
  int num=0,count=0;//num从键盘上得到数据,count用来记录循环次数
  for(count=0;count<5;count++)//循环5次
  {
   num=getchar();//从键盘输入数据
   printf("%c",num);
  }
}

   果然也只运行了3次,但不受什么字符不字符,整型不整型的影响,我想每次我输入一个字符完了,我要键入一个回车表示一次输入的结束,是不是输入流把回车也认为是一个字符了,然后我算了下,加上回车恰好够5次,然后我写以下代码测试:

#include <stdio.h>

void main()
{
  int num=0,count=0;//num从键盘上得到数据,count用来记录循环次数
  for(count=0;count<5;count++)//循环5次
  {
   num=getchar();//从键盘输入数据
   if(num=='/n')
   {
    count--;
    continue;
   }
   printf("%c",num);
  }
}

今天我读<<C专家编程>>的时候发现一节:
////////////////////////////////////////////////////////////////////////////////////////
(8.6节)                      不需要按回车键就能得到一个字符

    MS-DOS程序员在转到UNIX系统之后最先提出的问题之一就是“我如何在不按一下回车键的情况下从终端读取一个字符?”在UNIX中,终端输入在缺省情况下是被“一锅端”的,也就是说整行输入是被一起处理的,这样行编辑字符(backspace , delete等)可以不通过正在运行的程序就能发挥作用。通常,这是一种人们希望的方便方法,但它也意味着在读入数据时必须按一下回车键表示输入行结束后才能得到输入的数据。这种方法对于整行整行的输入是非常有效的,但有些程序需要在每按一键之后就得到这个字符,这就有些不方便了。
这个“一次输入一个字符的”特性对于许多种类的软件来说都是非常重要的,但对于PC而言却是小菜一碟。C函数库支持这个特性,通常使用一个kbhit()的函数,如果一个字符正在等待被读取,它就会发出提示。Microsoft ,Borland 的C编译器提供了了getch()(或getche(),它可以使字符在读取的同时回显在屏幕上来)来获取单个字符,而不用等整行结束。
    人们经常感到疑惑,为什么ANSI C 不定义一个标准的函数来获取一次按键后的字符。由于没有一种标准的方法,每个系统都采用了不同的方法,这们样便使程序失去了可移植性。反对将kbhit()纳入标准的人认为:它在绝大多数情况下用于游戏软件的,而且还存在其他许多未标准化的终端I/O特性。另外,你可能并不想要一个在某些操作系统中很难实现的标准库函数。赞成它的人则认为:它在绝大多数情况下用于游戏软件,而游戏编写者并不需要很多的标准化的其它终端I/O特性。不论你支持哪个观点,事实上X3J11小组还是错过了一个使C语言成为一代学生程序员在UNIX上编写游戏的一种选择的机会(就是未吸纳这个特性)。在UNIX中,有两种方法可以实现逐个字符的输入,一种很难,一种很容易。容易的方法就是让stty程序来实现这个功能。尽管它是一种间接实现的方法,但对程序而言并无大碍。

#include<stdio.h>

main()
{
 int c;
 /*最初终端驱动处于普通的一次一行模式*/

 system("stty raw");   /*使终端驱动处于一次一字符模式*/

 c=getchar();

 system("stty cooked");   /*使终端驱动回到一次一行模式*/
}


    最后一行system("stty cooked");是必要的,因为程序结束后,终端字符驱动特性的状态将延续下去。在程序把终端设为一种滑稽的状态之后,如果不作修改,它就会始终处于这种模式。这和设置环境变量明显不同,后者在进程结束后自动消失。

    把I/O设置为raw状态可以实现阻塞式读入(blocking read),如果终端没有字符输入,进程就一直等待,直到有字符输入为止。如果需要非阻塞式读入,可以使用ioctl()(I/O控制)系统调用。它提供一个针对终端特性的良好控制层,可以告诉你在SVr4系统下是否有一个键被按下。下面的代码使用了ioctl(),这样只有当一个字符等待被读和时进程才进行读取。这种类型的I/O被称为轮询,就好像不断地询问设备状态,看看它是否有字符要传给你。

#include<sys/filio.h>
int kbhit()
{
 int i;
 ioctl(0,FIONREAD,&i);
 return i;
}

main()
{
 int i=0; 
 int c=' ';
 system("stty raw -echo");
 printf("enter 'q' to quit: /n");
 for(;c!='q';i++)
 {
  if(kbhit())
  {
   c=getchar();
   printf("/n got %c,on iteration %d",c,i);
  } 
 }
 system("stty cooked echo");
}

//////////////////////////////////////////////////////////////////////////////////////////

    以上内容我一一做了测试,结果就是:kbhit()这个函数应该没什么用,因为在使用kbhit()的时候并未提供输入的机会,它只检测缓冲区中有无字符,如果有键按下,则返回对应键值;否则返回零。无论有无按键都会立即返回。而这个输入怎么提供呢,反正暂时还不会用。而getch(),getche()虽说提供了输入的机会,但使系统处于“阻塞等待状态”(应该可以这么说),系统一直等待输入,别的什么也做不了,假如你想做一个先前那样的打乒乓程序,就无法实现。还有一个问题是我在标准的C语言函数库查询手册中没查到上面的三个函数 ,但我在VC6.0中可以使用它们,只不过在编译时要出警告如下:
test.c(5) : warning C4013: 'kbhit' undefined; assuming extern returning int
test.c(10) : warning C4013: 'getche' undefined; assuming extern returning int
我不懂倒底是什么原因,谁能帮忙讲解下就好了。

    后面提出的那两种方法是UNIX中的,我又不会UNIX环境编程,所以暂时不理。最后作者提到了中断,我想那其实的确是一种方法,当然更高层的实现就是多线程了。这些我现在还不会做,用中断的话须用汇编,嗯,以后再做吧。不过今天解决了上次那个getchar()要等待回车的问题,因为我在最开始的代码中用getch()代替getchar(),循环就是5次了,也算有点收获。

------------------------all above based C-------------------------


 

### C语言中输入字符串直到用户按下回车键 在C语言中,`fgets()` 和 `scanf()` 是两种常用的用于输入字符串的方法。以下是它们的特点以及如何实现输入字符串直到用户按下回车键的功能。 #### 使用 `fgets()` 函数 `fgets()` 是一种安全的字符输入方法,可以从标准输入流(通常是键盘)读取一行字符,直到遇到换行符或者达到指定的最大长度为止[^1]。它的原型如下: ```c char *fgets(char *str, int n, FILE *stream); ``` - 参数说明: - `str`: 存储输入字符串的目标缓冲区。 - `n`: 表示最多可以读取字符数量(包括终止符 `\0`)。 - `stream`: 文件流指针,在这里通常为 `stdin`。 当用户输入字符串并按下回车时,`fgets()` 将停止读取并将换行符存储到缓冲区中(除非已经达到了最大长度)。因此,这种方法非常适合于需要保留换行符的情况。 示例代码: ```c #include <stdio.h> int main() { char str[100]; printf("请输入字符串(按回车结束):\n"); if (fgets(str, sizeof(str), stdin) != NULL) { // 输出带换行符的结果 size_t len = strlen(str); if (len > 0 && str[len-1] == '\n') { str[len-1] = '\0'; // 移除换行符 } printf("您输入字符串是:%s\n", str); } else { printf("输入失败。\n"); } return 0; } ``` #### 使用 `scanf()` 函数 虽然 `scanf("%s", str)` 可以用来读取字符串,但它会在遇到空白字符(如空格、制表符或换行符)时立即停止读取[^2]。这意味着如果希望接受带有空格的整个句子作为输入,则适合使用此方式。然而,可以通过 `%[` 转义序列来解决这个问题,例如: ```c scanf("%[^\n]", str); ``` 这表示让 `scanf()` 继续读取直到遇到换行符之前的所有字符。需要注意的是,这种方式会自动清除留在输入缓存中的换行符,可能会影响后续输入操作。 示例代码: ```c #include <stdio.h> #define MAX_LEN 100 int main(){ char str[MAX_LEN]; printf("请输入一段文字(按Enter键结束):\n"); scanf("%99[^\n]", str); // 防止溢出,留一位给'\0' getchar(); // 清理掉剩余的换行符 printf("您的输入为:%s\n", str); return 0; } ``` 尽管如此,相比起 `fgets()` 来说,这种做法稍显复杂且容易引发错误,推荐优先考虑前者。 ### 总结 为了更可靠地完成“输入字符串直到用户按下回车”的需求,建议采用 `fgets()` 方法,因为它仅能够很好地控制缓冲区大小从而防止越界访问,而且还能妥善处理各种边界情况下的特殊字符。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值