Outline:
· 字符串特性
· 字符串聲明
· 初始化方法
· I/O
· 字符串函數(shù)
————————
1.字符串常量
是用引號包裹的最域,通常作為printf() puts()的參數(shù),也可以用define來定義
如果字符串常量之間沒有間隔或間隔為空格锈麸,則ANSI C會把它們串聯(lián)起來加上一個空格镀脂。
字符串常量中使用雙引號需要轉(zhuǎn)義字符
字符串常量屬于靜態(tài)存儲類(static storage),如果函數(shù)中用到字符串常量忘伞,不管調(diào)用這個函數(shù)多少次薄翅,這個字符串常量在程序運行期間只存儲一份
- 字符串數(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個指針
- 字符串輸入
常用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
- 字符串輸出
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()略
- 自定義字符串輸入輸出函數(shù)
++優(yōu)先級比*高
while(*string != '\0')
while(*string) // 等價且beautiful
- 字符串函數(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]);
}
- 字符串轉(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
====================================================================
第一部分中用重定向讓程序和文件進行通信,但它完全和文件通信會失去與用戶交互的機會麸祷。所以需要文件通信方法讓我們可以在程序中打開文件然后用專門的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保存文本信息