# 9.C Formatted Input/Output
## 9.2 Streams

在進行輸入的時候,將bytes由設備(e.g.鍵盤,硬碟,網路等)傳到主記憶體中
三種streams
- standard input stream :通常連接至鍵盤滑鼠
- standard output stream :通常連結至螢幕
- standard error stream:通常也會顯示於螢幕之上
## 9.3 Formatting Output with printf
printf使用時會包含一個描述輸出格式的格式化字串
格式化字串
```c=
printf("format",variable);
```
format=%[flags][width][.piecision][length]type
- flags
- -:指定輸出靠左
- +:強制輸出正負號
- #:輸出8.16進位的前綴
- width
- 輸出寬度(小數點也算一格)
- .precision
- 規定小數點後面如何顯示
- %.nf 顯示n位
- length
- 同scanf的modifiers
:::danger
Common Programming Error 9.1
忘記將格式化字串用引號括起來是一種語法錯誤
>[name=蜜蜂]
:::
## 9.4 Peinting Integers
|Conversion specifier|Description|
|--------------------|-----------|
| d|signed十進位數字 |
| i|signed十進位數字 |
| o|unsigned八進位數字 |
| x or X|unsigned十六進位數字 |
| h, l or ll|長度修飾器 |
- signed vs unsigned
- signed可區分出具正負號的數值
- unsigned只儲存正數
```
NOTE:這裡i的使用與scanf不同
```
- x vs X
- x:在輸出的時候會輸出小寫的a~f
- X:在輸出的時候會輸出大寫的A~F
### 長度修飾器
放置於任何integer conversion specfier之前
- h:short integer
- l:long integer
- ll:long long integer
:::danger
Common Programming Error 9.2
用unsigned的conversion specifier 去print 負數值是錯誤的
>[name=蜜蜂]
:::
```c=
#include<stdio.h>
int main(void)
{
printf("%d\n", 455);
printf("%i\n", 455); // i same as d in printf
printf("%d\n", +455); // plus sign does not print
printf("%d\n", -455); // minus sign prints
printf("%hd\n", 32000);
printf("%ld\n", 2000000000L);// L suffix makes literal a long int
printf("%o\n", 455); // octal
printf("%u\n", 455);
printf("%u\n", -455);
printf("%x\n", 455); // hexadecimal with lowercase letters
printf("%X\n", 455); // hexadecimal with uppercase letters
}
```
```
455
455
455
-455
32000
2000000000
707
455
4294966841
1c7
1C7
```
## 9.5 Printing Floating-Point Number
轉換指定詞(conversion specifiers) e和E會將浮點數以指數記號(exponetial natation)
|Converssion specifier | Description|
|----------------------|------------|
|e or E |用指數表示小數點|
|f or F |以定點運算表示小數點|
|g or G |以值得大小決定要以e或是f來表示|
|L |放在任何浮點數轉換指定詞之前|
```
Note:F支援Visual Studio 2015年以後的Microsoft Visual C++ complier
```
### 9.5.1 Conversion Specifiers e,E and f
預設小數點右邊會有6位數的精確度
### 9.5.2 Conversion Specifiers g and G
轉換指定詞g(或G)會顯示沒有補上0的e(E)或f格式(例如1.234000會顯示成1.234)
:::danger
Error-Pervention Tip 9.1
當輸出資料時,請確定使用者察覺到資料會因為格式化而不是很精確(也就是指定精確度會產生四捨五入上的誤差)
:::
### 9.5.3 Demonstrating Floating-Point Conversion Specifiers
:::info
Portability Tip 9.1
某些編譯器的輸出中,指數的顯示會以加號(+)在家兩位數字的形式出現。
:::
```c
#include<stdio.h>
int main(void)
{
printf("%e\n", 1234567.89);
printf("%e\n", +1234567.89); // plus does not print
printf("%e\n", -1234567.89); // minus prints
printf("%E\n", 1234567.89);
printf("%f", 1234567.89); //six digits to right of decimal point
printf("%g\n", 1234567.89); // prints with lowercase e
printf("%G\n",1234567.89); // prints with uppercase E
}
```
```
1.234568e+006
1.234568e+006
-1.234568e+006
1.234568E+006
1234567.890000
1.23457e+--6
1.23457E+006
```
## 9.6 Printing Strings and Characters
c和s用來顯示個別字元和字串,轉換指定詞c必須使用char引數,轉換指定詞s的引數是指向char的指標。 s會不斷顯示字元,直到遇見空('\0')字元為止。
```c=
#include <stdio.h>
int main(void)
{
char character='A'; // initialize char
printf("%c\n",character);
printf("%s\n","This is a string");
char string[]="This is a string"; // initialize char array
printf("%s\n",string);
const char *stringPtr = "This is also a string"; // char pointer
printf("%s\n",stringPtr);
return 0;
}
```
```
A
This is a string
This is a string
This is also a string
```
:::danger
Common Programing Error9.3
使用%c顯示字串是一個錯誤。轉換指定詞%c希望收到char引數。但是字串是指向char的指標(也就是char*)
:::
:::danger
Common Programing Error9.4
使用%s來顯示char引數通常會造成執行錯誤,也就是記憶體逾越存取錯誤。%s希望收到的引數是指向char的指標
:::
:::danger
Common Programing Error 9.5
使用單引號括住你想呈現的字元字串會造成語法錯誤。字串必須用雙引號括住
:::
:::danger
Common Programing Error 9.6
用雙引號括住字元常數會產生一個含有兩個字元的字串的指標,其中第二個字元就是結束的空字元
:::
## 9.7 Other Conversion Specifiers
:::info
Portability Tip 9.2
轉換指定詞p會以實作環境定義的方式來顯示位置(大部分的系統都使用十六進位制表示,而不使用十進位制表示法)
:::
:::danger
Common Programing Error 9.7
嘗試顯示百分比字元,但是使用%而不是%%。當%出現的時候後面必須接著一個轉換指定詞。
:::
|Conversion specifier|Description|
|--------------------|-----------|
|p |使用實作環境所定義的方法顯示一個指標值。|
|% |顯示百分比字元|
```c=
#include <stdio.h>
int main(void)
{
int x = 12345;
int *ptr = &x;
printf("The value of ptr is %p\n",ptr);
printf("The address of x is %p\n\n", &x);
printf("Printgin a %% in a format control string\n");
return 0;
}
```
```
The value of ptr is 002EF778
The address of x is 002EF778
Printing a %in a format control string
```
## 9.8 Printing with Field Widths and Precision
欄位寬度大於要顯示的資料 則資料在欄位中會靠右對齊。 代表欄位寬度的整數,可以加入轉換指定詞中的百分比符號(%)與轉換指定詞之間(例如:%4d)。
### 9.8.1 Specifying Field Widths for Printing Integers
:::danger
Common Programing Error 9.8
要注意資料大小,顯示數值實沒有提供足夠的寬度,可能會造成其他資料顯示的位移。
:::
```c=
#include <stdio.h>
int main()
{
printf("%4d\n", 1);
printf("%4d\n", 12);
printf("%4d\n", 123);
printf("%4d\n", 1234);
printf("%4d\n", 12345);
printf("%4d\n", -1);
printf("%4d\n", -12);
printf("%4d\n", -123);
printf("%4d\n", -12345);
}
```
```
1
12
123
1234
12345
-1
-12
-123
-1234
-12345
```
## 9.9 Using Flags in the printf Format Control String
printf提供了flag(旗標)來輔助它的格式化輸出功能。同一個轉換指定詞中可以同時結合數個旗標。
| Flag|Description|
|----------|-----------|
| -|在指定欄位中將輸出靠左對齊。|
| +|在正值之前顯示正號,在負值之前顯示負號。|
| *space*|在正數之前顯示一空格,但不印+號。|
| #|和八進位轉換指定詞o一起使用時,書痴之前會加上0。和十六進位轉換指定詞x或X一起使用時,在輸出之前會加上0x或0X。沒有小數部分但是會以e, E, f, g或G顯示的浮點數來顯示小數點。(一般只有在含小數部分時才會顯示小數點。)對於g和G指定詞而言,小數點後面多餘的零不會消除。|
| 0|使用前導的零填補欄位。|
### 9.9.1 Right and Left Justification
```c=
#include <stdio.h>
int main(void)
{
puts("1234567890123456789012345678901234567890\n");
printf("%10s%10d%10c%10f\n\n", "hello", 7, 'a', 1.23);
printf("%-10s%-10d%-10c%-10f\n", "hello", 7, 'a', 1.23);
}
```
```
1234567890123456789012345678901234567890
hello 7 a 1.230000
hello 7 a 1.230000
```
### 9.9.2 Printing Positive and Negative Numbers with and without the + Flag
正號只有在適用+旗標時才會顯示出來。
```c=
#include <stdio.h>
int main(void)
{
printf("%d\n%d\n", 786, -786);
printf("%+d\n%+d\n", 786, -786);
}
```
```
786
-786
+786
-786
```
### 9.9.3 Using the Space Flag
注意,因為負號的關係,輸出值-547之前不會放置一個空白。
```c=
#include <stdio.h>
int main(void)
{
printf("% d\n% d\n", 547, -547);
}
```
```
547
-547
```
### 9.9.4 Using the # Flag
```c=
#include <stdio.h>
int main(void)
{
int c = 1427; // initialize c
printf("%#o\n", c);
printf("%#x\n", c);
printf("%#X\n", c);
double p = 1427.0; // initialize p
printf("\n%g\n", p);
printf("%#g\n", p);
}
```
```
02623
0x593
0X593
1427
1427.00
```
### 9.9.5 Using the 0 Flag
```c=
#include <stdio.h>
int main(void)
{
printf("%+09d\n", 452);
printf("%09d\n", 452);
}
```
```
+00000452
000000452
```
## 9.10 Printing Literals and Escape Sequences
escape Sequences(跳脫序列)會用反斜線(\)加上一個特別的escape character(跳脫字元)來表示。
|escape Sequences|Description|
|----------------|-----------|
| \\'|輸出(')|
| \\"|輸出(")|
| \\?|輸出(?)|
| \\\\ |輸出(\\)|
| \a|輸出可聽見的聲音|
| \b|游標往後移一位|
| \f|游標移到下一個邏輯頁的起始位置|
| \n|游標移至下一行|
| \r|游標移至目前所在行的起始位置|
| \t|游標移至下一個水平跳格的位置|
| \v|游標移至下一個垂直跳格的位置|
## 9.11 Reading Formatted Input with scanf
每個scanf敘述式都包含格式控制字串,它會用來描述要輸入資料的格式。格式控制字串包含轉換指定詞和字元常數。scanf函式具有以下的輸入格式化功能:
1.輸入所有型別的資料
2.從輸入資料流當中輸入指定的字元
3.跳過輸入資料流中的某些指定字元
### 9.11.1 scanf Syntax
```c=
scanf(format-control-string, other-arguments);
```
*format-control-string* : 描述輸入的格式
*other-arguments* : 一些指向變數的指標,這些變數會用來存放輸入的資料
:::success
Good Programming Practice 9.1
當輸入資料時,每次只提醒使用者輸入一個資料項或少量的資料即可。不要要求使用者在一個提示中輸入太多資料。
:::
:::success
Good Programming Practice 9.2
請考慮當(不是假設,是要當它真的會發生)輸入錯誤的資料時,使用者和你的程式應該做些什麼。例如:輸入了對程式來說無意義的整數值,或是字串中遺漏了標點和空白。
:::
### 9.11.2 scanf Conversion Specifiers
請注意,d和i轉換指定詞對利用scanf輸入來說有不同的意義,然而它們在以printf函式輸出時是可以互相交換的。
#### Integers
|Conversion specifier|Description|
|--------------------|-----------|
| d|讀進一個有正負號的十進位整數。對應的引數是指向整數的指標。|
| i|讀進一個有正負號的十進位,八進位或十六進位整數。對應的引數是指向整數的指標。|
| o|讀進一個八進位整數。對應的引數是指向無號整數的指標。|
| x or X|讀進一個十六進位整數。對應的引數是指向無號整數的指標。|
| h, l or ll|用於任何整數指定轉換之前,表示希望輸入short、long或long long的整數。|
#### Floating-point numbers
|Conversion specifier|Description|
|--------------------|-----------|
| e, E, f, g或G|讀進一個浮點數。對應的引數是指向浮點變數的指標。|
| l或L|用於任何浮點數轉換指定之前,表示希望輸入double或long double的浮點數。對應的引數是指向double或long double變數的指標。|
#### Characters and strings
|Conversion specifier|Description|
|--------------------|-----------|
| c|讀進一個字元。對應的引數是一個指向char的指標;不會加上空('\0')字元。|
| s|讀進一個字串。對應的引數是一個指向型別為char的陣列的指標;會自動增加空('\0')字元。|
#### Scan set
|Conversion specifier|Description|
|--------------------|-----------|
| [scan characters]|在一字串當中尋找存放在某陣列中的字元。|
#### Miscellaneous(其他)
|Conversion specifier|Description|
|--------------------|-----------|
| p|讀取一個在printf敘述式忠和%p的輸出具有相同型式的位址。|
| n|儲存目前為止scanf函式讀入的字元個數。對應的引數是指向整數的指標。|
| %|跳過輸入時的百分比符號(%)。|
### 9.11.3 Reading Integers with scanf
請注意,其中轉換指定詞%i可以用來輸入十進位制、八進位制、以及十六進位制的整數。
```c=
#include <stdio.h>
int main(void)
{
int a;
int b;
int c;
int d;
int e;
int f;
int g;
puts("Enter seven integers: ");
puts("\nThe input displayed as decimal integers is:");
printf("%d %d %d %d %d %d %d\n", a, b, c, d, e, f, g);
}
```
```
Enter seven integers:
-70 -70 070 0x70 70 70 70
The input displayed as decimal integers is:
-70 -70 56 112 56 70 112
```
### 9.11.4 Reading Floating-Point Numbers with scanf
```c=
#include <stdio.h>
// function main begins program execution
int main(void)
{
double a;
double b;
double c;
puts("Enter three floating-point numbers:");
printf("\nHere are the numbers entered in plain:");
puts("floating-point notation:\n");
}
```
```
Enter three floating-point numbers:
1.27987 1.27987e+03 3.38476e-06
Here are the numbers entered in plain floating-point notation:
1.279870
1279.870000
0.000003
```
### 9.11.5 Reading Characters and Strings with scanf
```c=
#include <stdio.h>
int main(void)
{
char x;
char y[9];
printf("%s", "Enter a string: ");
puts("The input was:\n");
printf("the character \"%c\" and the string \"%s\"\n", x, y);
}
```
```
Enter a string: Sunday
The input was:
the character "S" and the string "unday"
```
### 9.11.6 Using Scan Sets with scanf
一連串的字元可以用掃描集(scan set)來進行輸入。掃描集是格式控制字串中,一些由衷括號[]括起來,並且前面加上百分比符號的字元。掃描集會掃描輸入資料流中的字元,找出屬於此掃描集的字元。
注意,程式會讀入前輸入字串中的7個字母。第8個字母(h)不屬於掃描集,所以掃描的動作到此結束。
```c=
#include <stdio.h>
// function main begins program execution
int main(void)
{
char z[9]; // define array z
printf("%s", "Enter string: ");
scanf("%8[aeiou]", z); // search for set of characters
printf("The input was \"%s\"\n", z);
```
```
Enter string: ooeeooahah
The input was "ooeeooa"
```
#### Inverted scan set
用來掃描不再掃描集中的字元。只要在掃描集的中括號裡原來要掃描的字元前面加上一個脫字符號^(caret),就可以建立反掃描集。
```c=
#include <stdio.h>
int main(void)
{
char z[9];
printf("%s", "Enter a string: ");
printf("The input was \"%s\"\n", z);
}
```
```
Enter a string: String
The input was "Str"
```
### 9.11.7 Using Field Widths with scanf
用來從輸入資料流讀取指定個數的字元
```c=
#include <stdio.h>
int main(void)
{
int x;
int y;
printf("%s", "Enter a six digit integer: ");
scanf("%2d%d", &x, &y);
printf("The integers input were %d and %d\n", x, y);
}
```
```
Enter a six digit integer: 123456
The integers input were 12 and 3456
```
### 9.11.8 Skipping Characters in an Input Stream
scanf提供assignment suppression character(設定禁止字元),這個字元可使scanf從輸入的資料流中讀進任何型別的資料,並將它丟棄而不設定給變數。
```c=
#include <stdio.h>
int main(void)
{
int month = 0;
int day = 0;
int year = 0;
printf("%s", "Enter a date in the form mm-dd-yyyy: ");
printf("month = %d day = %d year = %d\n\n", month, day, year);
printf("%s", "Enter a date in the form mm/dd/yyyy: ");
printf("month = %d day = %d year = %d\n", month, day, year);
}
```
```
Enter a date in the form mm-dd-yyyy: 11-18-2012
month = 11 day = 18 year = 2012
Enter a date in the form mm/dd/yyyy: 11/18/2012
month = 11 day = 18 year = 2012
```
###### tags: `程設好難` `學校門口大樹石頭下` `他的課就很好睡阿`
<style>
.navbar-brand::after { content: " × 老葉的程式設計"; }
</style>