Microsoft PowerPoint - scanfCommonTraps.ppt

Similar documents
Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.06.doc

CC213

untitled

C/C++语言 - C/C++数据

Microsoft PowerPoint - 20-string-s.pptx

C 1 # include <stdio.h> 2 int main ( void ) { 4 int cases, i; 5 long long a, b; 6 scanf ("%d", & cases ); 7 for (i = 0;i < cases ;i ++) 8 { 9

CC213

C/C++基礎程式設計班

C/C++基礎程式設計班

2013 C 1 #include <stdio.h> 2 int main(void) 3 { 4 int cases, i; 5 long long a, b; 6 scanf("%d", &cases); 7 for (i = 0; i < cases; i++) 8 { 9 scanf("%

C 語言—陣列及字串

C/C++ 语言 - 循环

新・明解C言語入門編『索引』

2013 C 1 # include <stdio.h> 2 int main ( void ) 3 { 4 int cases, a, b, i; 5 scanf ("%d", & cases ); 6 for (i = 0;i < cases ;i ++) 7 { 8 scanf ("%d %d

untitled

Microsoft Word - chap13.doc

C 1

CC213

C

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.07.doc

( CIP) /. :, ( ) ISBN TP CIP ( 2005) : : : : * : : 174 ( A ) : : ( 023) : ( 023)

Microsoft Word - CPE考生使用手冊 docx

nooog

untitled

Microsoft Word - ACL chapter02-5ed.docx

c_cpp

C++ 程式設計

untitled

The return of scanf The number of fields successfully converted and assigned int a =1, b =2, c =3; int n = scanf("%d %d %d", &a, &b, &c); printf("%d\n

Ps22Pdf

Microsoft PowerPoint - ch09_AEL0080.ppt

3.1 num = 3 ch = 'C' 2

epub 33-8

C/C++语言 - 分支结构

第1章

Microsoft PowerPoint - C-Ch12.ppt

2015年计算机二级(C语言)模拟试题及答案(四)

1 Project New Project 1 2 Windows 1 3 N C test Windows uv2 KEIL uvision2 1 2 New Project Ateml AT89C AT89C51 3 KEIL Demo C C File

《计算概论》课程 第十九讲 C 程序设计语言应用

Microsoft Word finalSol.doc

, 即 使 是 在 昏 暗 的 灯 光 下, 她 仍 然 可 以 那 么 耀 眼 我 没 有 地 方 去, 你 会 带 着 我 么 杜 晗 像 是 在 嘲 笑 一 般, 嘴 角 的 一 抹 冷 笑 有 着 不 适 合 这 个 年 龄 的 冷 酷 和 无 情, 看 着 江 华 的 眼 神 毫 无 温

chap07.key

Microsoft PowerPoint - 04-array_pointer.ppt

华恒家庭网关方案

378高雄市都市計畫說明書

第1章

第一章

untitled

The golden pins of the PCI card can be oxidized after months or years

Python a p p l e b e a r c Fruit Animal a p p l e b e a r c 2-2

国信证券股份有限公司

星星排列 _for loop Protected Sub Page_Load(ByVal sender As Object, ByVal e As Dim h As Integer = 7 'h 為變數 ' Dim i, j As Integer For i = 1 To h

Microsoft Word - Chap02.doc

untitled

Microsoft Word 軟體設計第二部份範例試題_C++_ _1_.doc

目 錄

Microsoft Word - 04_object_Boxing_property_indexer.doc

Microsoft PowerPoint - ch04_AEL0080.ppt

Microsoft Word - C-pgm-ws2010.doc

0 0 = 1 0 = 0 1 = = 1 1 = 0 0 = 1

Ps22Pdf

Microsoft PowerPoint - C-Ch10.ppt

社大規畫-生活藝能期末報告.doc

C Programming

Microsoft Word - ACG chapter00c-3ed.docx

Microsoft Word - well_game.doc

Microsoft PowerPoint - assign1.ppt

Chapter 3 Camera Raw Step negative clarity +25 ] P / Step 4 0 ( 下一頁 ) Camera Raw Chapter 3 089

团 学 要 闻 我 校 召 开 共 青 团 五 届 九 次 全 委 ( 扩 大 ) 会 议 3 月 17 日, 我 校 共 青 团 五 届 九 次 全 委 ( 扩 大 ) 会 议 在 行 政 办 公 楼 五 楼 会 议 室 举 行, 校 团 委 委 员 各 院 ( 系 ) 团 委 书 记 校 学 生

Microsoft Word - Mail2000_SecurityPatch_

untitled

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

untitled

投影片 1

105 學 年 度 臺 中 市 立 國 民 中 學 藝 術 才 能 舞 蹈 班 學 生 招 生 暨 鑑 定 重 要 程 序 日 程 表 項 目 重 要 日 期 重 要 工 作 事 項 辦 理 單 位 簡 章 公 告 105 年 3 月 簡 章 暨 報 名 表 請 自 行 於 各 承 辦 網 站 下

臺北市立百齡高級中學九十二學年度第一學期代理教師甄選辦法草案

第九屆全國環境保護模範社區優良事蹟.PDF

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

C/C++基礎程式設計班


新版 明解C++入門編

untitled


51 C 51 isp 10 C PCB C C C C KEIL

Microsoft PowerPoint - C-Ch11.ppt

運算子多載 Operator Overloading

柳州化工股份有限公司

Microsoft PowerPoint - 12_StreamIO.ppt

untitled

任務二 : 產生 20 個有炸彈的磚塊, 放在隨機的位置編輯 Block 類別的程式碼 import greenfoot.; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) Write a description of class

<4D F736F F D20C9EEDBDACAD0BAA3D4C2CDA8D0C5B6AFCCACC3DCC2EBBDE2BEF6B7BDB0B82E646F63>

考 試 日 期 :2016/04/24 教 室 名 稱 :602 電 腦 教 室 考 試 時 間 :09: 二 技 企 管 一 胡 宗 兒 中 文 輸 入 四 技 企 四 甲 林 姿 瑄 中 文 輸 入 二 技 企 管 一

DaoCiDi2003TC ct-P293L02-R

bnb.PDF

Microsoft PowerPoint - 02 C語言基本概述.ppt

Searching and Sorting

Microsoft PowerPoint - 02 C語言基本概述.ppt

PowerPoint Presentation

download.kaoyan.com_2006ÄêÌì½ò¹¤Òµ´óѧ¸ß¼¶ÓïÑÔ³ÌÐòÉè¼Æ£¨409£©¿¼ÑÐÊÔÌâ

Transcription:

scanf 的緩衝區問題 與 scanf 支援的資料剖析功能 丁培毅 1

01 #include <stdio.h> 02 #include <stdlib.h> 03 04 int main() { 05 char str[100]; 06 char symbol='\0'; 07 問題 1 描述 08 printf("please input a string: "); 09 scanf("%s",str); 10 printf("please input a character as delimiter: "); 11 scanf("%c", &symbol); 12 printf("[%s][%c]\n", str, symbol); 13 14 system("pause"); 15 return 0; 16 } Please input a string: jasdlk;jfa<enter> Please input a character as delimiter: [jasdlk;jfa][ ] 請按任意鍵繼續... '\n' 為什麼程式執行起來第 11 列怎麼沒有停下來讓操作者輸入一個字元? Please input a string: hello world<enter> Please input a character as delimiter: [hello][ ] 請按任意鍵繼續... ' ' 是系統表現不穩定還是你誤會它的表現了? 2

慢速 I/O 裝置和快速 cpu 中間調節速度的區域 鍵盤緩衝區的問題 首先手 鍵盤 stdio 函式庫 (scanf) 的鍵盤緩衝區 變數 你在鍵盤上打 hello world<enter>, 都會在按下 <enter> 後被低階驅動程式搬進鍵盤緩衝區, 不會有東西消失了, 你也要特別注意換列字元 '\n', 只要按一次 <enter> 就會有一個 '\n' 字元進入緩衝區 其次, 所有在緩衝區裡的資料都是由程式中 scanf(), getchar(), getc(), gets() 來處理的 [ 請注意 getch(), getche(), 和 kbhit() 不是 stdio 函式庫裡的函式, 處理不到這個緩衝區裡面的資料 ] 接下來請注意需要完全了解 scanf() 每一個控制命令 scanf() 所做的動作, 例如 %s 是 跳過 0 或多個 white space, 由鍵盤緩衝區裡讀取連續不是 white space 的字元, 所謂 white space 包括空格, '\t', 和 '\n' 三個字元 又例如 %c 是 不跳過任何字元, 直接由鍵盤緩衝區裡讀取單一一個字元 ; %d 是跳過所有 white space, 由鍵盤緩衝區裡讀取連續 0~9 之間的十進位數字, 轉換為二進位, 如果除了 white space 之外只看到不是 0~9 的字元, scanf("%d",&x) 回傳 0 ( 注意是回傳 0 代表這個命令沒有成功, x 的數值不變 ) 了解上面這些以後你才會知道 scanf("%s",str); scanf("%c",&symbol); 當輸入 hello world<enter> 時 "hello" 會進入 str 陣列, 接下來的空格就進到 symbol 了, 所以才會覺得 scanf("%c",&symbol); 怎麼沒有停下來等候輸入 3

運用 scanf() 的 " %c" 格式字串, 控制 scanf 讀取 symbol 時需要 跳過所有的 white spaces 執行結果 printf("please input a string: "); scanf("%s",str); printf("please input a character as delimiter: "); scanf(" %c", &symbol); 也可以用 \n 或 \t 取代 Please input a string: asdfasdf Please input a character as delimiter: a [asdfasdf][a] 程式表現符合預期請按任意鍵繼續... 讀到空格後面的 'w' 了 Please input a string: hello world Please input a character as delimiter: [hello][w] 請按任意鍵繼續... 怎麼沒有讀到 world 還是沒有停下來讓操作者輸入一個字元? 4

運用 gets(str) 函式由鍵盤緩衝區讀取包含 white spaces 在內的一整列資料 ( 到第一個 '\n' 為止 ), str 陣列不包含 '\n', 但是 '\n' 會由鍵盤緩衝區中移除 printf("please input a string: "); gets(str); printf("please input a character as delimiter: "); scanf(" %c", &symbol); 執行結果 兩者功能一模一樣 Please input a string: hello world Please input a character as delimiter: [hello world][g] 請按任意鍵繼續... G 程式表現完全符合預期 讀出所有不是 '\n' 的資料, '\n' 還在緩衝區 ; 如果沒有任何資料的話, str 維持是空的 str[0] = 0; 或是 scanf("%[^\n]", str); scanf("%*1[\n]"); 由緩衝區讀出 1 個 '\n' 字元, 不存入任何變數 請注意想要跳過 '\n' 不要用 scanf("\n"); 這樣是跳過所有的 white space 5

運用 getchar() 及 fflush() 函式取代 scanf(" %c", ); 其中 fflush(stdin) 是由鍵盤緩衝區中移除所有資料, 此處就是 " world\n" 但是和 scanf(" %c", ); 的效果還是有一點點差別, 就是 getchar() 不會跳過額外的空格字元 printf("please input a string: "); scanf("%s",string); fflush(stdin); printf("please input a character as delimiter: "); symbol = getchar(); 執行結果 Please input a string: hello world Please input a character as delimiter: G [hello][g] 請按任意鍵繼續... 程式只能讀到 hello, 但是可以讀到 G 請注意 : fflush(stdin) 在線上測試系統 e-tutor 上, 在 linux GNU gcc/g++, 在 Mac clang gcc/g++ 都沒有作用, fflush( 輸出串流 ) 是有明確定義的動作, fflush( 輸入串流 ) 則沒有明確的定義, 請避免使用 6

不同鍵盤緩衝區問題 conio 的 getch(), getche(), kbhit() 是沒有緩衝區的輸入函式 iostream 的 cin >>, cin.get(), cin.getline(). cin.ignore() stdio 的 scanf(), getchar(), gets(), fflush() 兩個函式庫各有自己的鍵盤緩衝區 雖然可以藉由 ios_base::sync_with_stdio(true) 來同步, 但是一般來說 iostream 和 stdio 不要混著使用, 例如 std::ios_base::sync_with_stdio(false); scanf("%s", str1); std::cin >> str2; 在鍵盤上輸入 hello world<enter>every body<enter> 之後, (g++ only) str1 的內容是 hello, str2 的內容是 every 而不是 world 下面範例解釋 conio 和 stdio 共用的狀況 scanf("%s", str); c = getche(); 在鍵盤上輸入 hello world<enter>d 之後, (both g++ 4.8.1 and vc2010) str1 的內容是 hello, c 的內容是 d 而不是 w 7

問題 2 描述 01 #include <stdio.h> 02 #include <stdlib.h> 03 int main() { 04 float a; 05 printf("please input a floating number: "); 06 scanf("%f\n", &a); 07 printf("a = %f\n", a); 08 system("pause"); 09 return 0; 10 } Please input a floating number: 1.5<enter> <enter> 1<enter> a = 1.5 請按任意鍵繼續... 為什麼程式執行起來第 06 列讀了 1.5 進去以後一直停在那裡等候輸入不繼續執行呢? 一直按 <enter> 或是空白都沒有用 仔細看一下程式, 很多同學都會修改, 可以讓程式正確運作, 可是能不能給一個正確的解釋呢? 這樣子以後才不會又遇見一樣的錯誤啊! 換成這樣呢? 06 scanf("%f ", &a); 沒有辦法解釋的話, 可能還是你誤會它的表現了? 8

功能超級強大的 scanf scanf 這樣的函式不是從 C 才開始有的, Algol 68 裡面就有 readf 這樣的輸入解析 (input parsing) 函式, 大部分人都只知道 %d %u %lf %s %c 這些格式命令, 覺得需要和實際參數一一對應很麻煩, 而且需要用 & 運算子取得變數位址作為參數是不太好瞭解 很容易出錯的 所以一些入門的書乾脆不用 C 的 stdio 函式庫而用 C++ 裡面的 iostream 函式庫, 理由是簡單, 不需要解釋太多東西就可以順利運作 int x; double y; char z; char w[100]; cin >> x >> y >> z >> w; 就打發掉一切輸入了 可是其實這並不是 iostream 函式庫的用意, 它的設計是以物件化為主要目的, 使用它的話程式可以很容易地擴充任意物件輸入輸出序列化的功能, 維持封裝的完整性 ( 如果在使用的時候不知道上面的這些的話, 那就只是是為了簡單而用它, 也就需要接受它比 scanf 功能少很多很多的事實, 當然所有的功能都可以自己寫, 自己加上去的, 可是相信我在知道 scanf 強大的功能以後, 你不會想要這麼做的 ) 9

資料剖析範例 範例一 : 請讀取下列資料到三個浮點數陣列裡 double mandarin[2][2], math[2][2], english[2][2]; 1 年 1 班學生國文成績平均為 76.80 1 年 1 班學生數學成績平均為 68.00 1 年 1 班學生英文平均為 67.40 1 年 2 班學生英文平均為 68.80 1 年 2 班學生國文成績平均為 61.60 1 年 2 班學生數學成績平均為 57.00 2 年 1 班學生英文平均為 69.40 2 年 1 班學生國文成績平均為 71.80 2 年 1 班學生數學成績平均為 73.00 2 年 2 班學生國文成績平均為 75.60 2 年 2 班學生數學成績平均為 80.20 2 年 2 班學生英文平均為 59.80 請注意假設資料量很大, 格式與順序有點不太整齊, 不要手動編輯資料檔案來修改資料的格式, 是程式需要考量這些資料的變異性的 範例二 : 請讀取右側程式設定資料到下列陣列裡 char id[2][20]; char nickname[2][20]; int logintimes[2]; int lastloginyear[2]; int lastloginmonth[2]; int lastloginday[2]; [user] ID=giddens nickname= 九把刀 logintimes=868 lastlogin=20101226 [user] ID=bonddealer nickname= 總幹事 logintimes=32493 lastlogin=20100210 如果你曉得結構的用法的話, 也可以把這些資料讀到下面結構陣列裡 struct { char id[20], nickname[20]; int logintimes, lastloginyear, lastloginmonth, lastloginday; } user[2]; 請注意資料裡面 = 號以及前面的字串是給其它編輯器使用的, 讀進程式時比對正確即可, 不需記錄下來, 格式錯誤就直接結束程式 10

%c 讀取目前字元 scanf 格式命令用法 0 個或是多個 space,\t,\n %d, %lld, %x, %o: 跳過所有 white space (WS), 讀取 10/16/8 進位整數 %f, %lf: 跳過所有 WS, 讀取 10 進位浮點數, 例如 123.456e5 %s: 跳過所有 WS, 讀取任意非 WS 字串上述命令若目前字元為 WS 則一直等候輸入 ; 不是指定格式資料就提前結束 %n: 把此次 scanf 呼叫在緩衝區裡已經處理過 ( 讀入或是跳過 ) 的字元數轉換為整數 return value: 此次呼叫 scanf 成功讀取轉換為數值的資料筆數 ( 不包括 %n), 如果已經到達串流結尾則回傳 EOF (-1) %wc: 由目前字元讀取 w 個字元, 第一個字元存入變數中, 不夠 w 個字元時會等待使用者輸入 %wd, %wx, %wo, %ws, %wf: 跳過所有 WS, 讀取其後 w 個字元, 遇見不合法字元時, 將已讀入之資料轉換好, 提前結束 沒有 %w.pf 這種格式命令 11

scanf 格式命令用法 (cont d) space, \t, \n: 跳過所有 WS ( 請注意後兩者很容易以為是比對單一字元 ) 非 WS 的字元 c: 比對目前字元是否為指定字元, 是則跳過繼續處理其他格式命令, 否則提前結束此次 scanf ( 如何得知成功與否? 在命令之後加上 %n 命令, 檢查有沒有讀入目前字元數 ), c 與 %*1[c] 效果相同 %% 代表單一一個百分號 %w[a-za-z0-9,/] 讀入符合規則的最多 w 個字元到字串變數中 %*w[^0-9] 讀入最多 w 個 0-9 的字元, 不存到任何變數中 %*w[^ \t\n] 讀入最多 w 個不是 white space 的字元, 不存到任何變數中 注意 1. int ivar, char cvar; 當串流裡資料格式正確 scanf("%d", &ivar); scanf("%c", &cvar); 使得 %d 命令順利完成 scanf("%d%c", &ivar, &cvar); 時, 兩寫法是等效的 2. scanf 所有的參數都是記憶體位址, 連格式字串那個參數也是 3. fscanf() 和 sscanf() 所接受的命令和 scanf() 一樣, 一個由檔案串流裡讀取 / 剖析資料, 一個是由字元陣列 ( 字串 ) 裡讀取 / 剖析資料 12

資料格式命令範例 1. char c; scanf("%c",&c); // 不可以用 int c; scanf("%c",&c); 2. int d; scanf("%d",&d); scanf("%x",&d); scanf("%o",&d); 3. long long lld; scanf("%lld",&lld); 4. char buf[100]; scanf("%s",buf); // 請不要用 scanf("%s",&buf); 5. char buf[100]; scanf("%[a-z]",buf); // 讀入所有小寫字母的字元請注意用 %s 命令或是 %[xyz] 時變數一定要是字元陣列, 不可以是整數 浮點數 字元之類的, 編譯器不會檢查到, 可以運作但是會造成執行錯誤, 以及接續的記憶體內容被破壞 6. 上面是基本的 scanf 用法, 所有用法都可以在 % 之後加上 *, 告訴 scanf: 請根據命令處理資料串流, 但是讀到的資料不要放進任何變數裡 7. 所有的用法也可以在 % 和 * 之後加上一個數字來限定最多處理幾個輸入字元, 可以利用來處理特別的資料格式, 也可以用 char buf[51]; scanf("%50s",buf); 來避免 scanf 讀進來的東西寫到錯誤的記憶體位置去, 避免所謂的 buffer overflow attack 13

資料格式命令範例 (cont d) 8. char c; scanf(" %c",&c); 目的是為了讀取串流中接下來不是 white space 的一個字元, 有幾個等效的寫法讓你參考 "\n%c", "\t%c", " %1c", "%*[ \t\n]%1c", "%*[ \t\n]%1[^ \t\n]", 你不需要真的用這些怪怪的命令, 但是如果你看懂為什麼它們都有相同的功效, scanf 這些命令的精神你就掌握了一大半 9. 請注意 scanf(" %d",&d); 或是 scanf(" %s", buf); 命令裡的空格都是多餘的 ; scanf("%d ",&d); 或是 scanf("%s ", buf); 命令裡的空格會使得程式一直想要跳過 white space, 所以在讀到需要的資料之後, 你會發現一直按 <enter>, 空格時, scanf 都一直不結束, 不會繼續往下執行下一列的程式, 直到你按下不是 white space 的任意字元再加上 <enter>, scanf 才結束, 繼續往下執行下去, 當然 scanf("%d\n",&d); 也有相同的效果, 不要用錯了, 無法解釋程式的表現而以為電腦有問題 14

資料格式命令範例 (cont d) 10. char id[21]; sscanf("id=ab1_ab-9","id=%20[-_a-za-z0-9]",id); 這個範例裡一開始的 ID= 會比對成功所以繼續執行 %20[ ] 的命令, 讀取最多 20 個 a-z 之間或是 A-Z 之間或是 0-9 之間或是減號或是底線的字元到陣列 id 中, 所以陣列裡的資料會是 ab1_ab-9, 如果 ID= 沒有比對到, scanf 會提早結束, 回傳數值會是 0, 如果接下來完全沒有合法的文字 scanf 也會提早結束, 回傳數值會是 0, 如果合法的文字小於或是等於 20 個, 就會把合法的文字放到陣列 id 裡面, 如果合法的文字超過 20 個, 只會把前 20 個放到陣列 id 裡面, 只要有讀到一個以上的字元到陣列 id 中, 回傳數值就是 1 11. int a,nitems,nchars=-1; nitems=scanf("%d%n",&a, &nchars); 如果成功讀到了 a 的數值, nitems 的數值就是 1, 同時 nchars 裡面會是這一次 scanf 已經處理的字元數, 如果 a 沒成功讀取, 則 nitems 會是 0, nchars 會維持 -1, 如果讀取資料到變數 a 之前串流已經結束, nitems 會是 EOF (-1), nchars 會維持 -1 15

資料剖析範例 char name[20], tel[50], code[20], protocol[10], site[50], path[50]; int age; sscanf("name:john age:40 tel:(0912)123456", "name:%s age:%d tel:(%[0-9])%[0-9]", name, &age, code, tel); printf("%s %d %s-%s\n", name, age, code, tel); // john 40 0912-123456 sscanf("name:john age:40 tel:082-313530", "%*[^:]:%s %*[^:]:%d %*[^:]:%s", name, &age, tel); printf("%s %d %s\n", name, age, tel); // john 40 082-313530 sscanf("http://ccckmit.wikidot.com/cp/list/hello.txt", "%[^:]:%*2[/]%[^/]/%[a-zA-Z0-9._/-]", protocol, site, path); printf("protocol=%s site=%s path=%s\n", protocol, site, path); // protocol=http site=ccckmit.wikidot.com path=cp/list/hello.txt 應該用 // 16