# C 的格式化字串
我們輸出字串時,不一定只會是固定一堆字元,可能還會想把一些變數塞進去。那麼我們就需要在字串中加入一些記號,告訴電腦這裡需要插入變數,而那個記號稱為占位符。
在 C 語言中,我們會使用 `%` 來代表這個記號,後面會再加上一些符號,來告訴電腦要插入的變數的資料型態,以及要占多少寬度、置左還是置右、是否要補 0 之類的排版資訊。
## 資料型態
| 符號 | 說明 |
| :------------------ | :------------------------------ |
| `%d` / `%i` | 十進位整數 |
| `%u` | 無符號的十進位整數 |
| `%o` | 無符號的八進位整數 |
| `%x` / `%X` | 無符號的十六進位整數 |
| `%f` / `%F` | 十進位的浮點數 |
| `%e` / `%E` | 用科學記號表示浮點數 |
| `%g` / `%G` | 依照浮點數大小使用 `%f` 或 `%e` |
| `%a` / `%A` _(C99)_ | 十六進位的浮點數 |
| `%c` | 字元 |
| `%s` | 字串 |
| `%p` | 指標的記憶體位址 |
| `%n` | 什麼都不會輸出 |
| `%%` | 百分比符號(`%`) |
備註:
- 整數和浮點數格式化後,字母 (a-f) 的大小寫會根據符號的大小寫而定。
如: `%x` 會輸出 `6b`,而 `%X`會輸出 `6B`。
- `%f` 和 `%e` 會將小數點後的所有位數表示出來,即使全為 0 也會;而 `%g` 則會省略。
- `%a` 和 `%A` 於 C99 加入
- `%p` 只有小寫的形式,`%P` 不會轉成大寫字母,只會讓你編譯錯誤。
- `%n` 所需傳入的參數必須是 `int` 型別的指標。
- 由於 `%` 在這裡有著特殊的意義,所以要打兩次 (`%%`) 來讓電腦知道你只是想輸出 `%`,並沒有別的意思。
### 範例 1
```c=
int a = 2042, b = -100;
printf("%%d of a: %d\n", a);
printf("%%o of a: %o\n", a);
printf("%%x of a: %x\n", a);
printf("%%X of a: %X\n", a);
printf("\n");
printf("%%d of b: %d\n", b);
printf("%%u of b: %u\n", b);
```
輸出:
```
%d of a: 2042
%o of a: 3772
%x of a: 7fa
%X of a: 7FA
%d of b: -100
%u of b: 4294967196
```
### 範例 2
```c=
double c = 395.6, d = 123465790.123;
printf("%%f of c: %f\n", c);
printf("%%e of c: %e\n", c);
printf("%%E of c: %E\n", c);
printf("%%g of c: %g\n", c);
printf("%%a of c: %a\n", c);
printf("\n");
printf("%%f of d: %f\n", d);
printf("%%e of d: %e\n", d);
printf("%%g of d: %g\n", d);
```
輸出:
```
%f of c: 395.600000
%e of c: 3.956000e+02
%E of c: 3.956000E+02
%g of c: 395.6
%a of c: 0x1.8b9999999999ap+8
%f of d: 123465790.123000
%e of d: 1.234658e+08
%g of d: 1.23466e+08
```
### 範例 3
```c=
char chr = 'C';
char str[] = "在外面%一次就好了";
printf("%c\n", chr);
printf("這裡要兩次%%,%s\n", str);
```
輸出:
```
C
這裡要兩次%,在外面%一次就好了
```
### 範例 4
```c=
int n = 1;
printf("Address: %p\n", &n);
printf("There is nothing %n.\n", &n);
```
輸出:
```
Address: 0x7ffc8a370014
There is nothing .
```
## 資料長度
有時候我們會加上一些符號,來表示資料型態的大小。例如 `%d` 加上 `ll` 後變成 `%lld`,表示`long long int` 而非 `int`。
下面是一些常用的符號:
| 長度 | `d` / `i` | `u` / `o` / `x` / `X` | `f` / `e` / `g` / `a` | `n` |
| :--------- | :-------------- | :-------------------- | :-------------------- | ---------------- |
| (none) | `int` | `unsigned int` | `double` | `int*` |
| hh _(C99)_ | `signed char` | `unsigned char` | | `signed char*` |
| h | `short` | `unsigned short` | | `short*` |
| l | `long` | `unsigned long` | `doulbe` _(C99)_ | `long*` |
| ll _(C99)_ | `long long` | `unsigned long long` | | `long long*` |
| j _(C99)_ | `intmax_t` | `uintmax_t` | | `intmax_t*` |
| z _(C99)_ | signed `size_t` | `size_t` | | signed `size_t*` |
| t _(C99)_ | `ptrdiff_t` | unsigned `ptrdiff_t` | | `ptrdiff_t*` |
| L | | | `long doulbe` | |
備註:
- `hh`、`ll`、`j`、`z`、`t` 於 C99 加入
## 格式化
在 `%` 與表示資料型態的符號之間,還可以再加入一些東西,來指定輸出的格式。
| 符號 | 說明 |
| :-------- | :---------------------------------------------------------------------------- |
| - | 表示靠左對齊 (預設為靠右對齊) |
| + | 強制顯示數值的正負號 (預設只有負數會顯示) |
| _(space)_ | 如果沒有顯示正負號,就用顯示空格 |
| # | 如果是八進位,在數字前面加上 `0`;如果是十六進位,在數字前面加上 `0x` 或 `0X` |
| 0 | 表示在數字的左側補 0 |
| _數字_ | 表示最少要顯示多少字元。長度不足的話會用空格填補;超過的話不會被截斷 |
| _.數字_ | 表示浮點數在小數點後的位數 |
### 範例 1
```c=
int a = 12, b = 3465789;
printf("|%d|%+d|% d|\n", a, a, a);
printf("|%5d|%+5d|% 5d|\n", a, a, a);
printf("|%05d|%+05d|% 05d|\n", a, a, a);
printf("|%-5d|%-+5d|%- 5d|\n", a, a, a);
printf("%5d\n", b);
```
輸出:
```
|12|+12| 12|
| 12| +12| 12|
|00012|+0012| 0012|
|12 |+12 | 12 |
3465789
```
### 範例 2
```c=
double c = 123.465789;
printf("|%3f|\n", c);
printf("|%.3f|\n", c);
printf("|%8.3f|\n", c);
printf("|%8.0f|\n", c);
printf("%#.6a\n", c);
```
輸出:
```
|123.465789|
|123.466|
| 123.466|
| 123|
0x1.eddcf8p+6
```
## 延伸閱讀
其他更詳細的內容,可以參考說明文件:
- https://cplusplus.com/reference/cstdio/printf/
- https://en.cppreference.com/w/c/io/fprintf