03 格式化輸入/輸出
A programming language is low level when its programs require attention to the irrelevant.[1]
碼字不易,對(duì)你有幫助 點(diǎn)贊:thumbsup:/轉(zhuǎn)發(fā):arrow_right_hook:/關(guān)注 :eyes: ** 支持一下作者
微信搜公眾號(hào):不會(huì)編程的程序圓**
看更多干貨,獲取第一時(shí)間更新
想看更好的文章排版,可以閱讀原文:
請(qǐng)將本片與下一節(jié)《數(shù)據(jù)類型》 聯(lián)系起來一起“食用”。
注:本教程含有超綱內(nèi)容?。?!如果你看不懂,不要喪失信心,可以“不求甚解”一些,關(guān)鍵是要多寫代碼!然后繼續(xù)學(xué)習(xí)下面的內(nèi)容!
:world_map:思維導(dǎo)圖
:email:寫在前面
如果只是寫個(gè)人學(xué)習(xí)總結(jié)的博客很容易,簡(jiǎn)單寫一些感悟然后貼上代碼走人就可以了,甚至不用校審。但是我命名本系列為【C語言必知必會(huì)】幫助你從入門到精通 C語言,那勢(shì)必要“事無巨細(xì)”一些:既要考慮到?jīng)]有基礎(chǔ)的初學(xué)者,又不能止于基礎(chǔ)。所以本教程適合各類人群學(xué)習(xí),只要你看就一定會(huì)有幫助。
本教程是本人純手打并排版,校審由我與我的搭檔湯圓君一起完成的。你們看這一篇文章我要寫好幾個(gè)小時(shí)。如果文章對(duì)你有幫助,請(qǐng)不要“白嫖”。支持一下作者,作者會(huì)發(fā)更多干貨好文。
特別鳴謝:湯圓君(公眾號(hào):【Cc聽香水榭】 長(zhǎng)期更新高質(zhì)量英語教學(xué))關(guān)注她表示對(duì)她工作的認(rèn)可吧!
:arrow_forward: 此符號(hào)表示該內(nèi)容以后的章節(jié)會(huì)講解,此章節(jié)內(nèi)不要求理解。
:globe_with_meridians:目錄
@[toc]
printf 函數(shù)
printf()函數(shù)打印數(shù)據(jù)的指令要與待打印數(shù)據(jù)的類型相匹配。例如,打印整數(shù)時(shí)使用 %d,打印字符時(shí)使用 %c 。這些符號(hào)被稱為轉(zhuǎn)換說明(conversion specification),它們指定了如何把數(shù)據(jù)(以2進(jìn)制形式)轉(zhuǎn)換成可顯示的形式。
例如:
printf("I am %d years old", 18);
這是 printf()的格式:
printf(格式字符串,待打印項(xiàng)1,待打印項(xiàng)2,...);
待打印項(xiàng)都是要打印的的項(xiàng)。它們可以是變量,常量,甚至是在打印之前計(jì)算的表達(dá)式。上例中,只有一個(gè)待打印項(xiàng): 18 。
格式字符串包含兩種不同信息:
- 普通字符:以字符串中出現(xiàn)的形式打印出來。上例中,"I am" 與 " years old" 為普通字符
- 轉(zhuǎn)換說明:用待打印項(xiàng)的值來替換。上例中,"%d" 為轉(zhuǎn)換說明
:warning:
C語言的編譯器不會(huì)檢測(cè)格式字符串中轉(zhuǎn)換說明中的數(shù)量與待打印項(xiàng)總個(gè)數(shù)是否相匹配。
1.缺少參數(shù)
printf("%d %d\n", i); // wrong
printf 會(huì)正確顯示 i 的值,然后顯示一個(gè)無意義的整數(shù)值。
2.參數(shù)過多
printf("%d\n", i, j);// wrong
而在這種情況下,printf 函數(shù)會(huì)顯示變量 i 的值,但是不會(huì)顯示變量 j 的值
printf 轉(zhuǎn)換說明
轉(zhuǎn)換說明這部分我做了很久,比較詳細(xì),配合下一章數(shù)據(jù)類型才能看懂大部分,剩下的就需要你在不斷使用的過程中領(lǐng)悟了。

-
標(biāo)志(可選,允許出現(xiàn)多于一個(gè))
- 字段內(nèi)左對(duì)齊(默認(rèn)右對(duì)齊) + 在打印的數(shù)前加上 + 或 - (通常只有負(fù)數(shù)前面附上減號(hào))例1 空格 在打印的非負(fù)數(shù)前前面加空格( + 標(biāo)志優(yōu)先于空格標(biāo)志)例2 # 對(duì)象:八進(jìn)制數(shù),十六進(jìn)制數(shù),以g/G 轉(zhuǎn)換輸出的數(shù) 例3 0 用前導(dǎo) 0 在字段寬度內(nèi)對(duì)輸出進(jìn)行填充。如果轉(zhuǎn)換格式為d,i,o,u,x(X),而且指定了精度,可以忽略 0 例4 例 1:
printf("%d\n", 123); printf("%d\n", -123); printf("%+d\n", 123); printf("%+d\n", -123);123 -123 +123 -123例 2:
printf("% d\n", 123); printf("% d\n", -123); printf("% +d\n", 123);123 -123 +123例 3:
printf("%o\n", 0123); printf("%x\n", 0x123); printf("%#o\n", 0123); printf("%#x\n", 0x123); printf("%#g\n", 123.0); printf("%g\n", 123.0);123 123 0123 0x123 123.000 123例 4:
printf("%5d\n", 123); printf("%05d\n", 123); printf("%5.3d\n", 123);123 00123 123 -
最小字段寬度(可選)
如果數(shù)據(jù)項(xiàng)太小無法達(dá)到這個(gè)寬度,那么會(huì)對(duì)字段進(jìn)行填充。(默認(rèn)情況下會(huì)在數(shù)據(jù)項(xiàng)左側(cè)添加空格,從而使字段寬度內(nèi)右對(duì)齊)。
如果數(shù)據(jù)項(xiàng)過大以至于超過了這個(gè)寬度,那么會(huì)完整的顯示數(shù)據(jù)項(xiàng)。
字段寬度可以是整數(shù)也可以是字符
*。如果是字符 * ,那么字段寬度由下一個(gè)參數(shù)決定。如果這個(gè)參數(shù)為負(fù),它會(huì)被視為前面帶 - 標(biāo)志的正數(shù)。例5例 5:
printf("%5d\n", 123); printf("%2d\n", 123); printf("%*d\n", 5, 123); printf("%*d\n", -5, 123);123 123 123 123
-
精度(可選項(xiàng))
如果轉(zhuǎn)換說明是:
d,i,o,u,x,X, 那么精度表示最少位數(shù)(如果位數(shù)不夠,則添加前導(dǎo) 0 )
a,A,e,E,f,F(xiàn) ,那么精度表示小數(shù)點(diǎn)后的位數(shù)
g,G,那么精度表示有效數(shù)字個(gè)數(shù)
s,那么精度表示最大字節(jié)數(shù)
精度是由小數(shù)點(diǎn)(.)后跟一個(gè)整數(shù)或 * 字符構(gòu)成的。如果是 * ,那么精度由下一個(gè)參數(shù)決定(如果這個(gè)參數(shù)為負(fù),效果與不指定精度一樣。)如果只有小數(shù)點(diǎn),那么精度為0 。例 6
例 6:
printf("%.4d\n", 123); printf("\n"); printf("%f\n", 123.0); printf("%.1f\n", 123.0); printf("\n"); printf("%g\n", 123.0); printf("%.5g\n", 123.0); printf("\n"); printf("%s\n", "Hello"); printf("%.2s\n", "Hello"); printf("\n"); printf("%.*d\n", 4, 123); printf("%.*d\n", -4, 123);0123 123.000000 123.0 123 123 Hello He 0123 123 -
長(zhǎng)度修飾符(可選)。
長(zhǎng)度修飾符表明待顯示的數(shù)據(jù)項(xiàng)的長(zhǎng)度大于或小于特定轉(zhuǎn)換說明中的正常值。例7
長(zhǎng)度修飾符 轉(zhuǎn)換說明符 含義 hh (C99) d,i,o,u,x,X signed char, unsigned char h d,i,o,u,x,X short, unsigned short l d,i,o,u,x,X long, unsigned long ll (C99) d,i,o,u,x,X long long, unsigned long long L a,A,e,E,f,F(xiàn),g,G long double z (C99) d,i,o,u,x,X size_t j (C99) d,i,o,u,x,X ptrdiff_t 例 7:
printf("%#hhX\n", 0xAABBCCDDEEFF1122);//這是一個(gè)占用內(nèi)存為 8 個(gè)字節(jié)的十六進(jìn)制數(shù) printf("%#hX\n", 0xAABBCCDDEEFF1122); printf("%#X\n", 0xAABBCCDDEEFF1122); printf("%#lX\n", 0xAABBCCDDEEFF1122); printf("%#llX\n", 0xAABBCCDDEEFF1122);0X22 0X1122 0XEEFF1122 0XEEFF1122 0XAABBCCDDEEFF1122 -
轉(zhuǎn)換說明符
由于參數(shù)提升(:arrow_forward:),在實(shí)參傳遞給可變數(shù)量實(shí)參函數(shù)時(shí),float 會(huì)轉(zhuǎn)換為 double ,char 會(huì)轉(zhuǎn)換為 int。例8
轉(zhuǎn)換說明符 含義 d,i 把 int 類型轉(zhuǎn)換為 十進(jìn)制形式 o,u,x,X 把無符號(hào)整型轉(zhuǎn)換為八進(jìn)制(o),十進(jìn)制(u),十六進(jìn)制形式(x,X)。 f,F(xiàn) (F C99) 把 double 類型轉(zhuǎn)換為 十進(jìn)制形式,并把小數(shù)點(diǎn)放置在正確位置上。如果沒有指定精度,那么小數(shù)點(diǎn)后顯示6個(gè)數(shù)字。 e,E 把 double 類型轉(zhuǎn)換為 科學(xué)計(jì)數(shù)法形式。如果沒有指定精度,那么小數(shù)點(diǎn)后顯示6個(gè)數(shù)字。 g,G 把double 類型轉(zhuǎn)換為 f 形式或 e 形式。當(dāng)數(shù)值的指數(shù)部分小于 -4,或大于等于精度時(shí),會(huì)選擇以 e 的形式顯示。尾部的 0 不顯示(除非用#標(biāo)志),且小數(shù)點(diǎn)后跟有數(shù)字才會(huì)顯示出來。 a,A (C99) 把 double 類型轉(zhuǎn)換為十六進(jìn)制科學(xué)計(jì)數(shù)法(p計(jì)數(shù)法)。 c 顯示無符號(hào)字符的 int 類型值。 s 寫出由實(shí)參指向的字符串。 p 把 void* 類型轉(zhuǎn)換為可打印的形式。 n 相應(yīng)的實(shí)參必須是指向 int 型對(duì)象的指針。在該對(duì)象中存儲(chǔ) ...printf 函數(shù)已經(jīng)輸出的字符數(shù)量,不產(chǎn)生輸出。 % 寫字符 % 例 8:
printf("%i\n", 123); printf("%d\n", 123); printf("%o\n", 123); printf("%u\n", 123); printf("%x\n", 123); printf("%X\n", 123); printf("%f\n", 123.0); printf("%e\n", 123.0); printf("%g\n", 123.0); printf("%a\n", 123); printf("%c\n", 65); printf("%s\n", "123"); int* a = 2; printf("%p\n", a); printf("%%\n");輸出:為了方便大家觀看我已經(jīng)將輸出中的換行刪除了
123 123 173 123 7b 7B 123.000000 1.230000e+02 123 0x1.e13430000007bp-1021 A 123 00000002 %
printf() 返回值
返回值:傳輸?shù)捷敵隽鳎@示器)的字符數(shù),若出現(xiàn)輸出錯(cuò)誤或編碼錯(cuò)誤(對(duì)于字符串和字符轉(zhuǎn)換指定符)則為負(fù)值。
返回類型:
int使用場(chǎng)景:檢查輸出錯(cuò)誤。(看輸出的字符數(shù)是否正確)
#include<stdio.h>
int main(void) {
int count;
count = printf("Hello!\n");
printf("%d\n", count);
return 0;
}
輸出:
Hello!
7
打印較長(zhǎng)字符串
允許的換行方式:
printf("Hello %s\n",
XiaoHuang);//為了讓讀者知道該行未完,可以使用縮進(jìn)
錯(cuò)誤的換行方式:
printf("Hello
%s\n", XiaoHuang);
如果想在雙引號(hào)括起來的格式字符串中換行,應(yīng)該這樣寫:
printf("Hello"); printf (" %s\n", XiaoHuang);printf("Hello\ %s\n", XiaoHuang);printf("Hello" " %s\n", XiaoHuang);// ANSI C
方法1:使用多個(gè) printf 語句
方法2:在要換行的地方加上反斜杠( \ )來斷行。但是,下一行的代碼必須從該行最左端開始,不然輸出會(huì)包含你所縮進(jìn)的空白字符。
方法3:ANSI C 引入的字符串連接。C 編譯器會(huì)將多個(gè)字符串看作一個(gè)字符串。
scanf() 函數(shù)
我們從鍵盤輸入的都是文本,因?yàn)殒I盤只能生成文本字符:字符,數(shù)字和標(biāo)點(diǎn)符號(hào)。如果要輸入整數(shù) 2014,就要鍵入2,0,1,4.如果要將其存儲(chǔ)為數(shù)值而不是字符串,程序就必須要把字符依次轉(zhuǎn)換成數(shù)值,這就是 scanf() 要做的。
scanf() 把輸入的字符串轉(zhuǎn)換成整數(shù),浮點(diǎn)數(shù),字符和字符串,而 printf() 正好與之相反,把整數(shù),浮點(diǎn)數(shù),字符,字符串轉(zhuǎn)換成顯示在屏幕上的文本。
scanf() 與 printf() 類似,也要使用 格式字符串 和 參數(shù)列表。scanf() 中的格式字符串表明字符輸入流的目標(biāo)數(shù)據(jù)類型。兩個(gè)函數(shù)的主要區(qū)別在于參數(shù)列表中。printf() 函數(shù)使用變量,常量和表達(dá)式,而 scanf() 函數(shù)使用指向變量的指針?(:arrow_forward:)。這里不需要了解指針,只需要記住一下簡(jiǎn)單的兩條:
用 scanf 讀取
- 基本變量類型的值,在變量名前加上一個(gè)
& - 把字符串讀入數(shù)組中,不要使用
&
下面的程序演示了這兩條規(guī)則:
input.c —— 何時(shí)使用 &
#include<stdio.h>
int main(void){
int age;
float assets;
char pets[30];//字符數(shù)組,存放字符串
printf("Enter you age, assets and you favorite pet.\n");
scanf("%d %f", &age, &assets); // 這里要用 &
scanf("%s", pets);// 字符數(shù)組不使用 &
return 0;
}
:warning:
初學(xué)者在使用 scanf 時(shí),在應(yīng)該寫 & 的時(shí)候容易忽略 & ,所以每次使用 scanf 的時(shí)候一定要格外小心。通常情況下,必要的地方缺少 & 會(huì)讓程序崩潰(編譯器沒有警告),但是也有時(shí)候程序并不會(huì)崩潰,這時(shí)候找 bug 可能會(huì)讓你頭痛。
scanf 的 長(zhǎng)度修飾符 和 轉(zhuǎn)換說明符 與 printf 幾乎相同。主要的區(qū)別如下:
-
長(zhǎng)度修飾符 :(可選項(xiàng))對(duì)于 float 與 double 類型,printf() 的轉(zhuǎn)換說明都用
f; 而對(duì)于 scanf() ,float 保持不變,double 要在 f 前加長(zhǎng)度修飾符 l ,即:lf。例 1例 1:
#include<stdio.h> int main(void) { double a = 3.0; scanf("%lf", &a); printf("%lf", a); return 0; }
-
轉(zhuǎn)換說明符 :
%[集合]匹配集合中的任意序列;%[^集合]匹配非集合中的任意序列。例 2例 2:
#include<stdio.h> int main(void) { char str[10];//字符串?dāng)?shù)組 scanf("%[123]", str); printf("%s", str); return 0; } //輸入:123456abc123 //輸出:123int main(void) { char str[10];//字符串?dāng)?shù)組 scanf("%[^123]", str); printf("%s", str); return 0; } //輸入:abc4123a //輸出:abc4
-
字符
*:(可選項(xiàng))字符 * 出現(xiàn)意味著賦值屏蔽(assignment suppression): 讀入次數(shù)據(jù)項(xiàng),但是不會(huì)將其賦值給對(duì)象。用 * 匹配的數(shù)據(jù)項(xiàng)不包含在 ...scanf 函數(shù)返回的計(jì)數(shù)中。例 3例 3:
#include<stdio.h> int main(void) { int a = 0; scanf("%*d%d", &a); printf("%d", a); return 0; } 輸入:1 2 輸出:2
-
最大字段寬度:(可選項(xiàng))最大字段寬度限制了輸入項(xiàng)中的字符數(shù)量。如果達(dá)到最大值,那么次數(shù)據(jù)項(xiàng)的轉(zhuǎn)換結(jié)束。轉(zhuǎn)換開始跳過的空白不計(jì)。例 4
//輸入:1234 Hello //先猜測(cè)一下輸出 #include<stdio.h> int main(void) { int a = 0; char str[10]; scanf("%2d%3s", &a, str); printf("%d %s", a, str); return 0; } //輸出:12 34
進(jìn)一步思探究 scanf()
在上面了解了 scanf 的基本情況后,我們進(jìn)一步探究 scanf 函數(shù)。
上面的例 2,為何只是輸出了 "123", 我們明明還輸入了一組 123,為什么沒有輸出呢?
scanf 函數(shù)如果發(fā)生了 輸入失敗(沒有字符輸入)或 匹配失敗 (即輸入字符和格式串不匹配),那么...scanf 會(huì)提前返回。返回就意味著這個(gè) scanf 的讀入結(jié)束。
scanf 返回的又是什么呢?
成功賦值的接收參數(shù)的數(shù)量(可以為零,在首個(gè)接收用參數(shù)賦值前匹配失敗的情況下),或者若輸入在首個(gè)接收用參數(shù)賦值前發(fā)生失敗,則為EOF(EOF 的值是 -1)。
在C程序中測(cè)試 scanf 函數(shù)的返回值的循環(huán)很普遍。例如,下面的循環(huán)逐一讀取一串整數(shù),在首個(gè)遇到問題的符號(hào)處停止:
while(scanf("%d", &i) == 1){
...
}
對(duì)于 scanf 部分最開始的程序 input.c
如果我們這樣先輸入:
18 98.5
diandian
再這樣輸入:
18
98.5
diandian
如果你添加上 printf 語句輸出這三項(xiàng),會(huì)發(fā)現(xiàn),這兩種輸入的輸出是一樣的。
在尋找起始位置時(shí),scanf 函數(shù)會(huì)忽略空白字符(white-space character,包括空格符,水平和垂直制表符,換頁符和換行符),但是%[ , %c, %n除外。例 5
例 5:
#include<stdio.h>
int main(void) {
char ch = 'a';
char str[10] = "hi";
scanf("%c", &ch);
scanf("%[123]", str);
printf("%c %s", ch, str);
return 0;
}
//輸入: b (輸入的是:空格 + b,然后按下回車鍵想接著輸入下一個(gè) scanf)
//輸出: hi
這個(gè)例子除了證明了上面的結(jié)論,還說明了:
但是 scanf 函數(shù)會(huì)忽略最后的換行符,實(shí)際上它沒有讀取它,這個(gè)換行符時(shí)下一次 scanf 函數(shù)讀入的第一個(gè)字符。
scanf 函數(shù)遵循什么規(guī)則來識(shí)別整數(shù)或浮點(diǎn)數(shù)呢?
在要讀入整數(shù)時(shí),scanf 函數(shù)首先會(huì)尋找正號(hào)或負(fù)號(hào),然后從讀入一個(gè)數(shù)字開始直到讀入一個(gè)非數(shù)字為止。
當(dāng)要求讀入浮點(diǎn)數(shù)時(shí),scanf 函數(shù)首先會(huì)尋找正號(hào)或負(fù)號(hào)(可選),然后是一串?dāng)?shù)字(可能含有小數(shù)點(diǎn)),再后是一個(gè)指數(shù)(可選)。指數(shù)由一個(gè)字母e,可選的符號(hào),一個(gè)或多個(gè)數(shù)字組成。
當(dāng) scanf 函數(shù)遇到一個(gè)不可能輸入當(dāng)前項(xiàng)的字符時(shí),它會(huì)把此字符“放回原處”,以便在掃描下一項(xiàng)或下一次調(diào)用 scanf 時(shí)再次讀入。思考下面(公認(rèn)有問題的)4個(gè)數(shù)的排列:
1-20.3-4.0e3回車
然后我們用這個(gè) scanf 函數(shù)來讀入:
scanf("%d%d%f%f", &i, &j, &x, &y);
scanf 會(huì)如何處理這組輸入呢?
- %d :讀入 1
- %d :讀入 -20
- %f :讀入 .3 (當(dāng)作 0.3 處理)
- %f:讀入 剩下的輸入。但是不讀入最后的回車
使用 %s 轉(zhuǎn)換說明,scanf 會(huì)讀取除了空白字符以外的所以字符。scanf 跳過空白字符并開始讀入第一個(gè)非空白字符,保存非空白字符直到再遇到空白字符結(jié)束。這意味著,scanf 最多只能讀取一個(gè)單詞。無法利用字段寬度使得 scanf 讀取多個(gè)單詞,scanf 會(huì)在字段寬度結(jié)束或遇到空白字符處停止。scanf 將字符串放入數(shù)組時(shí),會(huì)在字符串序列末尾加上一個(gè) \0。
格式串中的普通字符
-
空白字符:...scanf 函數(shù)格式串中的一個(gè)或多個(gè)連續(xù)的空白字符與輸入流中的零個(gè)或多個(gè)空白字符匹配。
簡(jiǎn)單說一下就是,格式串中有空格,輸入時(shí)你可以不寫空格或?qū)懚鄠€(gè);格式串中有多個(gè)空格,輸入時(shí)你可以只寫一個(gè)空格。
-
非空白字符:看個(gè)程序就明白了:
#include<stdio.h> int main(void) { int i, j, k; printf("Enter a date: "); scanf("%d - %d - %d", &i, &j, &k); printf("date: %d - %d - %d", i, j, k); return 0; }//輸入: Enter a date: 2020 - 2-22 //輸出: date: 2020 - 2 - 22
**空格你可以隨便空,換行都可以隨便換,但是一定要打 ''-'' 符號(hào)。**
易混淆的 printf() 與 scanf()
-
printf("%d", &i);輸出的并不是 i 的值 (而是 i 的地址的十進(jìn)制數(shù)值)
-
scanf("%d, %d", &i, &j);scanf 在第一個(gè) %d 讀入一個(gè)整數(shù)后,試圖把逗號(hào)與輸入流中的下一個(gè)字符相匹配,如果這個(gè)字符不是
,,那 scanf 就會(huì)終操作,而不再讀取變量 j 的值。 -
scanf("%d\n", &i);printf 函數(shù)中經(jīng)常有
\n,但是如果在 scanf 格式串結(jié)尾放一個(gè) \n 通常會(huì)引發(fā)你預(yù)期之外的問題。對(duì)于 scanf 函數(shù)來說,\n 等同于空格,那么 scanf 就會(huì)在流中尋找空白字符,但是我們上面說過,scanf 格式串中的空白字符會(huì)與 輸入流中的零個(gè)或多個(gè)空白字符匹配。所以當(dāng)你輸入完成后按下回車,這個(gè)回車會(huì)與 scanf 中的 \n 匹配,之后你無論打多少回車都不會(huì)使 scanf 結(jié)束,除非你輸入一個(gè)非空字符,使 scanf 因匹配失敗而退出。
參考資料:《C Primer Plus》《C語言程序設(shè)計(jì):現(xiàn)代方法》
本文GitHub已收錄,所有教學(xué)和練習(xí)代碼都會(huì)上傳上去。
https://github.com/hairrrrr/C-CrashCourse
如果對(duì)你有幫助,請(qǐng)點(diǎn)一個(gè) star:star: 呦~ ? 感謝!:love_letter:
以上就是本次的內(nèi)容。
如果文章有錯(cuò)誤歡迎指正和補(bǔ)充,感謝!
最后,如果你還有什么問題或者想知道到的,可以在評(píng)論區(qū)告訴我呦,我可以在后面的文章加上你們的真知灼見:eye:。
關(guān)注我,看更多干貨!
我是程序圓,我們下次再見。:fallen_leaf:
-
任何編程語言在處理無關(guān)事務(wù)時(shí)都是低級(jí)語言。 ?