C Primer Plus——2. 字符串 & 文件I/O

Outline:
· 字符串特性
· 字符串聲明
· 初始化方法
· I/O
· 字符串函數(shù)
————————
1.字符串常量
是用引號包裹的最域,通常作為printf() puts()的參數(shù),也可以用define來定義
如果字符串常量之間沒有間隔或間隔為空格锈麸,則ANSI C會把它們串聯(lián)起來加上一個空格镀脂。
字符串常量中使用雙引號需要轉(zhuǎn)義字符
字符串常量屬于靜態(tài)存儲類(static storage),如果函數(shù)中用到字符串常量忘伞,不管調(diào)用這個函數(shù)多少次薄翅,這個字符串常量在程序運行期間只存儲一份

  1. 字符串數(shù)組及初始化
    可以申請足夠大的空間來放字符串
const char m1[40] = "hannahs"; //會自動加結(jié)束符,如果用一個一個字符來初始化氓奈,要記得自己加空字
// 符'\0'带饱,否則只是字符數(shù)組而不是字符串

規(guī)定數(shù)組大小時至少要比字符數(shù)大一坦仍,多出來的位置編譯器會初始化為空字符
也可以讓編譯器決定數(shù)組大小罢防。
用指針符號和用數(shù)組符號建立字符串都可以仗扬,指針符號允許自增自減而數(shù)組名不可以,字符串都是在靜態(tài)存儲區(qū)育勺,在程序運行時為數(shù)組分配空間并把字符復制到數(shù)組中光羞,而指針的話在程序運行時復制了字符串的地址。初始化分別如下

char heart[] = "I love China.";
char * head = "I love Suzhou.";

兩者都可以用數(shù)組符號[]索引來取值化借,都可以用指針加法來取值(*(heart+i), *(head+i))捡多,但只有指針可以自增

heart = head; // illegal
head = heart; //legal指針指向數(shù)組是可以的

指針用數(shù)組符號和索引進行修改時,會有Bus error內(nèi)存訪問錯誤
字符串數(shù)組

char * mystrs[5] = {"dafew","2dsfw","3sdf","4ds","5cs"}; // 數(shù)組存5個指針蒜焊,指針指向字符串
//的第一個字符泳梆。雙引號用來初始化一個指針

可以用mystrs[0][0]表示第一個字符串的首字母
于是优妙,可以用二維數(shù)組來創(chuàng)建字符串數(shù)組

char mystrs2[5][81];

這樣內(nèi)存大小就固定了套硼,指針數(shù)組的話長度是不固定的由初始化字符串決定長度邪意。一個放5個完整的字符串,另一個放5個指針

  1. 字符串輸入
    常用printf(), gets(), fgets()
    (1) gets()
    以換行符為分割萌朱,函數(shù)讀取換行符后把結(jié)束符代替換行符呆贿。下一次的讀取就從新的一行開始. gets()返回值是char指針,它的函數(shù)原型在stdio.h中
    gets()的構(gòu)造如下:
char * gets( char * s){
  ...
  return s;
}// 如果讀取出錯會返回NULL
// so the error detection could be
while(gets(name)!=NULL){...}

(2) fgets()
gets()不檢查存儲區(qū)能不能放下實際輸入的數(shù)據(jù)做入,多出來的溢出到相鄰內(nèi)存區(qū)竟块,所以不安全。fgets()有參數(shù)指定大小蒋情,還有參數(shù)指定從哪個文件輸入棵癣。

fgets(name, MAX, stdin); // MAX指定最多可讀入MAX-1個字符狈谊,stdin指定從鍵盤讀數(shù)據(jù)

fgets()讀取直到換行符河劝,會存下?lián)Q行符,which is different from gets()赎瞎,所以需要額外的動作來定位且刪除換行符
(3) scanf()
從第一個非空白字符開始讀务甥,讀到(但不包括)下一個空白字符.如果指定了字段寬度如%10s缓呛,則讀10個字符或先遇到了空白字符哟绊。scanf的返回值為成功讀取的項目數(shù),或者遇到文件結(jié)束時返回EOF

char name1[11], name2[11];
printf("Enter 2 names\n");
scanf("%5d %10d", name1, name2);
printf("There are %s and %s", name1, name2);
輸入>>>Portensia Callowit
輸出>>>There are Porte and nsia
  1. 字符串輸出
    puts(), fputs(), printf()
    (1) puts
#define DEF "I am a string."
int main(){
  char * str1 = "A pointer was initialized.";
  char str2[80] = "An array was initialized.";
  puts("arguments to put"); // puts()會自動在字符串結(jié)尾加換行符,雙引號中的字符是常量票髓,并被看作地址
  puts(DEF);
  puts(str1);
  puts(str1+4); // 從第5個符號開始打印到最后
  puts(str2);
  puts(&str2[4]);
}

puts()在遇到空字符的時候停下來,一般字符串初始化會有空字符洽沟,但是字符數(shù)組是沒有空字符的!
(2) fputs()
需要第二個參數(shù)說明寫到哪個文件裆操,用stdout輸出到屏幕。
fputs不加換行符踪区!

char line[81];
while(gets(line)){
  puts(line);} //讀取一行昆烁,在下一行回顯
-------------------
char line[81];
while(fgets(line,81,stdin)){
  fputs(line,stdout);} // 回顯在同一行

(3) printf()略

  1. 自定義字符串輸入輸出函數(shù)
    ++優(yōu)先級比*高
while(*string != '\0')
while(*string) // 等價且beautiful
  1. 字符串函數(shù)
    string.h
    (1) strlen(a)
    不計'\0'
    (2) strcat(a, b)
    把b的副本添加到a的后面,達到字符串連接的效果静尼,返回第一個字符串
    (3) strncat(a, b, n)
    strcat不檢查第一個參數(shù)是否能容納連接后的字符串。strncat規(guī)定最多可以添加n個字符
    (4) strcmp(a, b)
    如果直接拿str1==str2的話,不能進行字符串比較眷细,因為這里比的是指針普舆,除非指向同一個地方等號才會成立池磁。所以字符串的比較通過strcmp()楷兽,如果相同就返回0, 返回正數(shù)說明a的ascii值大于b
    ascii 65 為A芯杀,97為a
    字符是可以直接比較的
    (5) strncmp(a, b, n)
    對前n個字符進行比較
    (6) strcpy & strncpy(target, src)
    字符串復制, 把src指向的字符串復制到target指向的數(shù)組中, target需要分配好空間端考,不能亂指。返回值是target(第一個參數(shù))的值(一個地址)
// 判斷字符串是否以s開頭
if(temp[0] == 's')
if(strncmp(temp, "s", 1) == 0) // 等價

strcpy()和gets()一樣揭厚,不檢查目標是否能容納源字符串却特。strncpy()用第三個參數(shù)來規(guī)定最大可復制的字符數(shù)
(7) sprintf(target, "...%...", list)
跟printf差不多,多了第一個參數(shù)目的字符串地址筛圆,為了輸出到該地址instead of 屏幕
(8) strchr(target, c)
在目標字符串中尋找第一個字符c的位置裂明,找到則返回該地址,否則返回空指針
(9)strrchr(target, c)
返回一個指針太援,指向目標串中最后一次出現(xiàn)c的地方
(10) strpbrk(const * char s1, const * char s2)
返回一個指針闽晦,指向了s2中任意一個字符which最早出現(xiàn)在s1中的位置
(11) strstr(s1,s2)
返回一個指針扳碍,指向s2第一次出現(xiàn)在s1中的位置(在1中找2)

// use fgets and remove \n
    printf("Enter your name:\n");
    char name[MAX];
    fgets(name, MAX-1, stdin);
    char * find;
    find = strchr(name,'\n');
    *find = '\0';
    puts(name);
// 輸入多個字符串,判斷輸入結(jié)束
    char input[MAX][40];
    int cnt_in=0;
    char tmp[40];
    while (cnt_in<MAX && gets(tmp)!=NULL && tmp[0]!='\0')
    {
        strcpy(input[cnt_in++], tmp);
    }
    for(int i=0;i<cnt_in;i++){
        puts(input[i]);
    }
  1. 字符串轉(zhuǎn)數(shù)字
    int atoi(str);
    atoi可以轉(zhuǎn)換字符串形式的數(shù)據(jù)仙蛉,甚至可以轉(zhuǎn)換"42dsa"這樣的笋敞,取開頭數(shù)字部分。如果沒有可以轉(zhuǎn)換的數(shù)字荠瘪,可能返回0
    atof()轉(zhuǎn)為double
    atol()轉(zhuǎn)為long
    ANSI C提供更復雜的函數(shù)夯巷,strtol(3個參數(shù))轉(zhuǎn)為long, strtoul(3個參數(shù))轉(zhuǎn)為unsigned long, strtod(2個參數(shù))轉(zhuǎn)為double,它們復雜在可以識別并報告字符串中非數(shù)字部分的第一個字符
long strtol(char * ptr, char ** endptr, int base);
char * end;
long num = strtol(argv[1], &end, 10); // 以10進制看待字符串數(shù)字哀墓,第二個參數(shù)是將指向結(jié)束指針的地址

當然趁餐,逆向有itoa, ftoa

====================================================================
\Large\color{LIghtSkyBlue}{文件I/O}
第一部分中用重定向讓程序和文件進行通信,但它完全和文件通信會失去與用戶交互的機會麸祷。所以需要文件通信方法讓我們可以在程序中打開文件然后用專門的I/O函數(shù)來讀取寫入文件

C將文件看成連續(xù)的字節(jié)序列澎怒,文件的兩種視圖:文本視圖、二進制視圖
MS-DOS:/r/n
Macintosh: /r
C : /n

fopen()
有兩個參數(shù)阶牍,第一個是文件名(包含文件名字符串的地址)喷面,第二個是打開模式

model string meaning
"r" open and read file
"w" open and write, 先將文件長度截為0,如果不存在就創(chuàng)建
"a" open and write, 在文件尾追加內(nèi)容走孽,如果不存在就創(chuàng)建
"r+" open 可以更新也可以寫入

//各種搭配+號惧辈,我也不懂。磕瓷。帶b的表示以二進制模式打開
如果用w打開文件盒齿,會清空文件原來的內(nèi)容
fopen函數(shù)返回FILE指針,并不指向?qū)嶋H的文件困食,而是指向關(guān)于文件信息的數(shù)據(jù)包边翁,可以知道緩沖區(qū)位置和緩沖區(qū)相關(guān)信息
磁盤不夠、文件名非法硕盹、存取權(quán)限不夠或硬件問題都可能導致fopen函數(shù)執(zhí)行失敗

getc() & putc()
與getchar() putchar()相似符匾,需要告訴getc() putc()所使用的文件

ch = getc(fp); // 從fp指向的文件中得到一個字符
putc(ch, fp); // 寫入
putc(ch, stdout); 
putchar(ch); // equal
char ch;
FILE * fp;
fp = fopen("words","r");
while((ch = getc(fp))!=NULL){
  putchar(ch);
}

fclose(fp)
關(guān)閉成功返回0,否則返回EOF

標準文件指針,都是FILE指針類型
stdin
stdout
stderr

文件IO函數(shù)
(1)fprintf()

char words[MAX];
fprintf(stderr,"Close file failed.\n");
while(fscanf(fp,"%s",words)==1){ // 會自動掃描文件從頭到尾
  puts(words); // 回顯
}

(2) fscanf()

while(fscanf(fp,"%s",words)==1){
/*code*/}

other: puts() fputs() gets() fgets()
以上都是順序存取的IO函數(shù)

隨機存却窭:fseek() & ftell()
fseek()有3個參數(shù)啊胶,第一個是FILE指針,第二個參數(shù)是offset且是Long類型的(數(shù)字加L)整數(shù)垛贤,第三個參數(shù)是起點模式
SEEK_SET:文件開始
SEEK_CUR:當前位置
SEEK_END:文件結(jié)尾
第二個參數(shù)為負時表示從起點向開頭走N個字節(jié)
正常的話fseek返回0焰坪,如果試圖移動超出文件范圍,會返回-1

ftell返回long類型的文件當前位置聘惦。某饰。/* 不會 */

long count, last;
fseek(fp, 0L, SEEK_END); // 定位到文件尾
last = ftell(fp); // 返回文件的字節(jié)長度
for(count; count<=last; count++){
  fseek(fp, -count, SEEK_END);
  ch = getc(fp); // fp會變??露乏?
}

在二進制模式下碧浊,C不支持SEEK_END模式
/* 懵 */

fgetpos() & fsetpos()
Long的范圍大概在正負20億,fseek ftell處理大文件還是有問題的
這倆函數(shù)用fpos_t這個新類型來代表位置
fgetpos()函數(shù)原型:

int fgetpos(FILE * restrict stream, fpos_t * restrict pos);

函數(shù)在pos位置上放置一個fpos_t值瘟仿,成功返回0箱锐,否則返回非零

int fsetpos(FILE *steam, const fpos_t *pos);

int ungetc(int c, FILE * fp);

    char word[10];
    getchar(); // 消耗一個字符
    ungetc('a',stdin); // 把a放回輸入流, 嘗試文件指針不得行
    scanf("%s",word);
    puts(word); // 得到a開頭的單詞

int fflush(FILE * fp);
把緩沖區(qū)未寫的數(shù)據(jù)發(fā)到fp劳较,如果fp是空指針,就刷新輸出緩沖

setvbuf()

二進制IO
int fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * fp);

// 保存一個256字節(jié)大小的數(shù)據(jù)對象
char buffer[256];
fwrite(buffer, 256, 1, fp);
// 保存10個double值的數(shù)組
 double earnings[10];
fwrite(earnings, sizeof(double), 10, fp);

//fread
fread(earnings, sizeof (double), 10, fp);// 把文件中的值復制到數(shù)組中

函數(shù)原型中ptr 是void類型因為數(shù)組類型不一定观蜗,void是可以cast到其他類型的

if (feof(fp)){
//文件讀取結(jié)束(feof返回非0值)后執(zhí)行的代碼
}
if(ferror(fp)){
//文件讀取錯誤(ferror返回非0值)后執(zhí)行的代碼
}

fread fwrite可以保留精度
getc fprintf保存文本信息

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市墓捻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌砖第,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梧兼,死亡現(xiàn)場離奇詭異,居然都是意外死亡渡紫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唧喉,“玉大人复哆,你說我怎么就攤上這事腌零√菡遥” “怎么了益涧?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵锈锤,是天一觀的道長。 經(jīng)常有香客問我,道長久免,這世上最難降的妖魔是什么浅辙? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮阎姥,結(jié)果婚禮上记舆,老公的妹妹穿的比我還像新娘。我一直安慰自己呼巴,他們只是感情好泽腮,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衣赶,像睡著了一般诊赊。 火紅的嫁衣襯著肌膚如雪府瞄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天遵馆,我揣著相機與錄音鲸郊,去河邊找鬼。 笑死团搞,一個胖子當著我的面吹牛严望,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播复隆,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拨匆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挽拂?” 一聲冷哼從身側(cè)響起惭每,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎亏栈,沒想到半個月后台腥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡绒北,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年黎侈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闷游。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡峻汉,死狀恐怖贴汪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情休吠,我是刑警寧澤扳埂,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站瘤礁,受9級特大地震影響阳懂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柜思,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酝蜒。 院中可真熱鬧,春花似錦堕澄、人聲如沸霉咨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至喷斋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浆西,已是汗流浹背顽腾。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留久信,地道東北人漓摩。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓幌甘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锅风。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354