<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 練習24:輸入輸出和文件 > 原文:[Exercise 24: Input, Output, Files](http://c.learncodethehardway.org/book/ex24.html) > 譯者:[飛龍](https://github.com/wizardforcel) 你已經學會了使用`printf`來打印變量,這非常不錯,但是還需要學習更多。這個練習中你會用到`fscanf`和`fgets`在結構體重構建關于一個人的信息。在這個關于讀取輸入的簡介之后,你會得到C語言IO函數的完整列表。其中一些你已經見過并且使用過了,所以這個練習也是一個記憶練習。 ```c #include <stdio.h> #include "dbg.h" #define MAX_DATA 100 typedef enum EyeColor { BLUE_EYES, GREEN_EYES, BROWN_EYES, BLACK_EYES, OTHER_EYES } EyeColor; const char *EYE_COLOR_NAMES[] = { "Blue", "Green", "Brown", "Black", "Other" }; typedef struct Person { int age; char first_name[MAX_DATA]; char last_name[MAX_DATA]; EyeColor eyes; float income; } Person; int main(int argc, char *argv[]) { Person you = {.age = 0}; int i = 0; char *in = NULL; printf("What's your First Name? "); in = fgets(you.first_name, MAX_DATA-1, stdin); check(in != NULL, "Failed to read first name."); printf("What's your Last Name? "); in = fgets(you.last_name, MAX_DATA-1, stdin); check(in != NULL, "Failed to read last name."); printf("How old are you? "); int rc = fscanf(stdin, "%d", &you.age); check(rc > 0, "You have to enter a number."); printf("What color are your eyes:\n"); for(i = 0; i <= OTHER_EYES; i++) { printf("%d) %s\n", i+1, EYE_COLOR_NAMES[i]); } printf("> "); int eyes = -1; rc = fscanf(stdin, "%d", &eyes); check(rc > 0, "You have to enter a number."); you.eyes = eyes - 1; check(you.eyes <= OTHER_EYES && you.eyes >= 0, "Do it right, that's not an option."); printf("How much do you make an hour? "); rc = fscanf(stdin, "%f", &you.income); check(rc > 0, "Enter a floating point number."); printf("----- RESULTS -----\n"); printf("First Name: %s", you.first_name); printf("Last Name: %s", you.last_name); printf("Age: %d\n", you.age); printf("Eyes: %s\n", EYE_COLOR_NAMES[you.eyes]); printf("Income: %f\n", you.income); return 0; error: return -1; } ``` 這個程序非常簡單,并且引入了叫做`fscanf`的函數,意思是“文件的格式化輸入”。`scanf`家族的函數是`printf`的反轉版本。`printf`用于以某種格式打印數據,然而`scanf`以某種格式讀取(或者掃描)輸入。 文件開頭沒有什么新的東西,所以下面只列出`main`所做的事情: ex24.c:24-28 創建所需的變量。 ex24.c:30-32 使用`fgets`函數獲取名字,它從輸入讀取字符串(這個例子中是`stdin`),但是確保它不會造成緩沖區溢出。 ex24.c:34-36 對` you.last_name`執行相同操作,同樣使用了`fgets`。 ex24.c:38-39 使用`fscanf`來從`stdin`讀取整數,并且將其放到`you.age`中。你可以看到,其中使用了和`printf`相同格式的格式化字符串。你也應該看到傳入了`you.age`的地址,便于`fsnaf`獲得它的指針來修改它。這是一個很好的例子,解釋了使用指向數據的指針作為“輸出參數”。 ex24.c:41-45 打印出用于眼睛顏色的所有可選項,并且帶有`EyeColor`枚舉所匹配的數值。 ex24.c:47-50 再次使用了`fscanf`,從`you.eyes`中獲取數值,但是保證了輸入是有效的。這非常重要,因為用戶可以輸入一個超出`EYE_COLOR_NAMES`數組范圍的值,并且會導致段錯誤。 ex24.c:52-53 獲取`you.income`的值。 ex24.c:55-61 將所有數據打印出來,便于你看到它們是否正確。要注意`EYE_COLOR_NAMES`用于打印`EyeColor`枚舉值實際上的名字。 ## 你會看到什么 當你運行這個程序時,你應該看到你的輸入被適當地轉換。你應該嘗試給它非預期的輸入,看看程序是怎么預防它的。 ```sh $ make ex24 cc -Wall -g -DNDEBUG ex24.c -o ex24 $ ./ex24 What's your First Name? Zed What's your Last Name? Shaw How old are you? 37 What color are your eyes: 1) Blue 2) Green 3) Brown 4) Black 5) Other > 1 How much do you make an hour? 1.2345 ----- RESULTS ----- First Name: Zed Last Name: Shaw Age: 37 Eyes: Blue Income: 1.234500 ``` ## 如何使它崩潰 這個程序非常不錯,但是這個練習中真正重要的部分是,`scanf`如何發生錯誤。對于簡單的數值轉換沒有問題,但是對于字符串會出現問題,因為`scanf`在你讀取之前并不知道緩沖區有多大。類似于`gets`的函數(并不是`fgets`,不帶`f`的版本)也有一個我們已經避免的問題。它并不是道輸入緩沖區有多大,并且可能會使你的程序崩潰。 要演示`fscanf`和字符串的這一問題,需要修改使用`fgets`的那一行,使它變成`fscanf(stdin, "%50s", you.first_name)`,并且城市再次運行。你會注意到,它讀取了過多的內容,并且吃掉了你的回車鍵。這并不是你期望它所做的,你應該使用`fgets`而不是去解決古怪的`scanf`問題。 接下來,將`fgets`改為`gets`,接著使用`valgrind`來執行`valgrind ./ex24 < /dev/urandom`,往你的程序中輸入一些垃圾字符串。這叫做對你的程序進行“模糊測試”,它是一種不錯的方法來發現輸入錯誤。這個例子中,你需要從`/dev/urandom`文件來輸入一些垃圾,并且觀察它如何崩潰。在一些平臺上你需要執行數次,或者修改`MAX_DATA`來使其變小。 `gets`函數非常糟糕,以至于一些平臺在程序運行時會警告你使用了`gets`。你應該永遠避免使用這個函數。 最后,找到`you.eyes`輸入的地方,并移除對其是否在正確范圍內的檢查。然后,為它輸入一個錯誤的數值,比如-1或者1000。在`Valgrind`執行這些操作,來觀察會發生什么。 > 譯者注:根據最新的C11標準,對于輸入函數,你應該總是使用`_s`后綴的安全版本。對于向字符串的輸出函數,應該總是使用C99中新增的帶`n`的版本,例如`snprintf`。如果你的編譯器支持新版本,就不應該使用舊版本的不安全函數。 ## IO函數 這是一個各種IO函數的簡單列表。你應該查詢每個函數并為其創建速記卡,包含函數名稱,功能和它的任何變體。 + `fscanf` + `fgets` + `fopen` + `freopen` + `fdopen` + `fclose` + `fcloseall` + `fgetpos` + `fseek` + `ftell` + `rewind` + `fprintf` + `fwrite` + `fread` 過一遍這些函數,并且記住它們的不同變體和它們的功能。例如,對于`fscanf`的卡片,上面應該有`scanf`、`sscanf`、`vscanf`,以及其它。并且在背面寫下每個函數所做的事情。 最后,為了獲得這些卡片所需的信息,使用`man`來閱讀它的幫助。例如,`fscanf`幫助頁由`man fscanf`得到。 ## 附加題 + 將這個程序重寫為不需要`fscanf`的版本。你需要使用類似于`atoi`的函數來將輸入的字符串轉換為數值。 + 修改這個程序,使用`scanf`來代替`fscanf`,并觀察有什么不同。 + 修改程序,是輸入的名字不包含任何換行符和空白字符。 + 使用`scanf`編寫函數,按照文件名讀取文件內容,每次讀取單個字符,但是不要越過(文件和緩沖區的)末尾。使這個函數接受字符串大小來更加通用,并且確保無論什么情況下字符串都以`'\0'`結尾。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看