不按回車就接受一個字符輸入!

 
                                                     不按回車就接受一個字符輸入!
     記得前幾天的一篇日誌(http://blog.csdn.net/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-------------------------

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1114543

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章