標籤

2008年9月8日 星期一

C 字串

http://oaunix.hlhs.hlc.edu.tw/~program/hkin/string.htm#a8

簡 介 下一節 到頁頂


C 沒 有 字 串 型 態 , 因 此 我 們 要 用 字 元 陣 列 來 處 理 字 串 , 例 如 :


char string[] = { 'H', 'e', 'l', 'l', 'o' };

i 0 1 2 3 4
word[i] 'H' 'e' 'l' 'l' 'o'

如 果 要 顯 示 這 個 字 串 , 就 要 用 printf 和 for 迴 圈 逐 個 字 元 顯 示 。

例 子 :

void print_string(char string[], int length) {
int i;

for (i = 0 ; i < i =" 0;" st1 =" %s\nst2" st1 =" Hello" st2 =" World">

main() {
printf("Press Enter to quit\n");
while (getchar() != '\n') ;
}


說 明 :


每 次 呼 叫 getchar 函 數 都 會 等 候 按 鍵 , 按 鍵 後 傳 回 該 鍵 。


例 子 :

#include <stdio.h>

void read_line(char string[], int max_length) {
char ch;
int i = 0;

do {
ch = getchar();
string[i] = ch;
i++;
}
while (ch != '\n' && i <= max_length); string[i-1] = '\0'; }

main() { char string[21]; read_line(string, 20); printf("%s", string); }

執 行 結 果 : This is a line. This is a line.

說 明 : 輸 入 字 串 。
void read_line(char string[], int max_length) 用 read_line 函 數 來 輸 入 字 串 , 可 以 控 制 每 個 輸 入 的 字 元 , 比 scanf 更 有 彈 性 , max_length 是 字 元 數 目 上 限 , 用 來 避 免 字 串 溢 滿 。 while (ch != '\n' && i <= max_length) 如 果 按 Enter 鍵 或 i 大 過 max_length 就 離 開 。 string[i-1] = '\0'; 在 字 串 結 尾 加 入 '\0' 。 其 它 字 串 語 法 上一節 下一節 到頁頂 有 時 一 個 字 串 太 長 , 你 想 分 幾 行 來 寫 它 , 可 以 在 一 行 的 結 尾 寫 反 斜 號 , 例 如 : char lower[] = "abcdefghijklmnopqrstuvwxyz"; 可 以 寫 成 : char lower[] = "abcdefghij\ klmnopqrst\ uvwxyz"; 留 意 字 串 會 由 下 一 行 的 開 頭 繼 續 的 , 所 以 如 果 寫 : char lower[] = "abcdefghij\ klmnopqrst\ uvwxyz"; 就 會 變 成 : char lower[] = "abcdefghij klmnopqrst uvwxyz"; 還 有 另 一 種 更 方 便 的 寫 法 , 就 是 把 一 個 字 串 寫 成 多 個 獨 立 的 字 串 , 例 如 : "OneTwoThree" 可 以 寫 成 : "One" "Two" "Three" 所 以 lower 陣 列 也 可 以 寫 成 : char lower[] = "abcdefghij" "klmnopqrst" "uvwxyz";  

實 例 上一節 下一節 到頁頂 實 例 : 字 數 統 計 上一節 下一節 到頁頂 一 般 文 字 處 理 程 式 都 有 字 數 統 計 功 能 。 假 設 每 個 英 文 字 都 是 由 一 列 連 逐 的 英 文 字 母 組 成 , 例 如 : This is a line. 可 分 為 「 This 」 「 is 」 「 a 」 「 line 」 4 個 字 , 而 : This's a line. 其 中 「 This's 」 可 分 為 「 This 」 「 s 」 兩 個 字 , 因 為 單 引 號 不 是 英 文 字 。

例 子 :

int is_alpha(char ch) { return ('a' <= ch && ch <= 'z') ('A' <= ch && ch <= 'Z'); }
int count_word(char string[]) { int i, count, looking_for_word; count = 0; looking_for_word = 1;

for (i = 0 ; string[i] != '\0' ; i++) { if ( is_alpha(string[i]) ) { if (looking_for_word) { looking_for_word = 0; count++; } } else { looking_for_word = 1; } } return count; } main() { char st[] = "Wow! Great job."; printf( "%s\nWords: %i", st, count_word(st) ); }

執 行 結 果 : Wow! Great job. Words: 3

說 明 : 字 元 可 分 為 兩 類 : 「 英 文 字 母 」 (Alphabet) 和 非 英 文 字 母 , 統 計 方 法 就 是 由 左 至 右 逐 個 字 元 處 理 , 如 果 字 元 不 是 英 文 字 母 , 就 等 待 英 文 字 母 出 現 , 一 出 現 就 把 字 數 加 一 , 然 後 就 不 用 再 等 待 , 直 至 字 元 不 是 英 文 字 母 為 止 。 i   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 string[i]   'W' 'o' 'w' '!' ' ' 'G' 'r' 'e' 'a' 't' ' ' 'j' 'o' 'b' '.' '\0' is_alpha(string[i])   1 1 1 0 0 1 1 1 1 1 0 1 1 1 0 0 looking_for_word 1 0 0 0 1 1 0 0 0 0 0 1 0 0 0 1 1 count 0 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3   int is_alpha(char ch) is_alpha 函 數 檢 查 一 個 字 元 是 否 英 文 字 母 。

looking_for_word 如 果 looking_for_word 是 1 , 表 示 正 等 待 著 下 一 個 字 , 即 是 等 待 英 文 字 母 。 if ( is_alpha(string[i]) ) { if (looking_for_word) { looking_for_word = 0; count++; } 如 果 現 正 等 待 著 下 一 個 字 , 而 string[i] 又 是 英 文 字 母 的 話 , 就 把 字 數 加 一 , 與 及 不 用 等 待 下 一 個 字 。 looking_for_word = 1; 如 果 string[i] 不 是 英 文 字 母 , 就 等 待 下 一 個 字 的 來 臨 。  

實 例 : 基 本 字 串 處 理 上一節 下一節 到頁頂 實 例 : 基 本 字 串 處 理 : 字 串 長 度 上一節 下一節 到頁頂

例 子 :
int string_length(char string[]) { int i; i = 0; while (string[i] != '\0') i++; return i; } main() { char st[21] = "012345"; printf("st = %s ; Length = %i", st, string_length(st)); } 執 行 結 果 : st = 012345 ; Length = 6

說 明 : while (string[i] != '\0') i++; 當 字 元 不 是 '\0' , 就 把 該 字 元 計 算 入 長 度 。

實 例 : 基 本 字 串 處 理 : 字 串 複 製 上一節 下一節 到頁頂 複 製 基 本 型 態 的 資 料 可 以 用 等 於 符 號 , 不 過 它 不 適 用 於 陣 列 , 即 是 你 不 能 寫 : string1 = string2; 所 以 複 製 字 串 必 須 逐 個 字 元 複 製 。 例 子 : void string_copy(char to[], char from[]) { int i; i = 0; while ( (to[i] = from[i]) != '\0' ) i++; } main() { char st1[21] = "012345", st2[21] = "abcde"; printf("Before: st2 = %s\n", st2); string_copy(st2, st1); printf("After : st2 = %s\n", st2); } 執 行 結 果 : Before: st2 = abcde After : st2 = 012345 說 明 : while ( (to[i] = from[i]) != '\0' ) i++; 與 「 字 串 長 度 」 例 子 差 不 多 , 也 是 檢 查 字 元 是 否 '\0' 。 這 句 會 先 執 行 : to[i] = from[i] 然 後 才 把 from[i] 的 字 元 與 '\0' 比 較 , 即 是 先 複 製 , 後 檢 查 , 因 此 在 迴 圈 完 結 時 , to 字 串 結 尾 會 有 '\0' 。

實 例 : 基 本 字 串 處 理 : 字 串 比 較 上一節 下一節 到頁頂 你 也 不 能 寫 : if (string1 == string2) 來 比 較 字 串 , 而 正 確 的 做 法 就 是 把 兩 個 字 串 內 的 所 有 對 應 的 字 元 作 比 較 , 如 果 有 一 個 字 元 不 同 的 話 , 就 算 是 不 相 等 了 , 例 如 : char string1[] = "abcde"; char string2[] = "abdde"; 因 為 string1[2] 是 'c' , string2[2] 是 'd' , 所 以 它 們 不 相 等 。 其 實 , 字 串 也 可 以 好 像 數 目 一 樣 , 把 兩 樣 東 西 作 比 較 , 會 有 3 種 情 況 : 等 於 、 大 於 及 小 於 。

什 麼 ? 文 字 也 有 大 小 之 分 嗎 ? 對 啊 , 還 記 得 ASCII 碼 嗎 ? 每 個 字 元 都 有 一 個 碼 , 我 們 可 以 跟 據 這 個 碼 來 決 定 字 元 的 大 小 , 而 比 較 方 法 很 簡 單 , 只 需 把 兩 個 字 串 由 頭 至 尾 逐 個 字 元 作 比 較 便 可 , 所 謂 的 「 由 頭 至 尾 」 , 就 是 由 指 數 0 的 字 元 開 始 , 直 至 出 現 不 同 的 字 元 , 或 者 到 了 字 串 結 尾 。 為 什 麼 不 是 「 由 尾 至 頭 」 呢 ? 可 能 沒 有 實 際 用 途 吧 , 因 為 「 由 頭 至 尾 」 可 以 把 英 文 字 排 列 到 好 像 字 典 一 樣 。 例 如 比 較 以 上 的 string1 和 string2 , 首 先 比 較 指 數 0 字 元 , 兩 者 都 是 'a' , 然 後 比 較 指 數 1 字 元 , 兩 者 都 是 'b' , 然 後 比 較 指 數 2 字 元 , string1[2] 是 'c' , 它 的 ASCII 碼 是 63h , 而 string2[2] 是 'd' , 它 的 ASCII 碼 是 64h , 所 以 'd' 比 'c' 大 , 即 是 string2 比 string1 大 。 我 們 可 以 寫 個 函 數 , 分 別 傳 回 0 、 1 和 -1 來 代 表 等 於 、 大 於 及 小 於 , 例 如 : int string_compare(char st1[], char st2[]); 情況 傳回 st1 等 於 st2 0 st1 大 於 st2 1 st1 小 於 st2 -1  

例 子 : int string_compare(char st1[], char st2[]) { int i; i = 0; while (st1[i] == st2[i] && st1[i] != '\0') i++; if (st1[i] == st2[i]) { return 0; } else { return (st1[i] > st2[i])? 1 : -1;
}
}

main() {
char st1[21] = "abcde", st2[21] = "abdde", st3[21] = "abcdefg";

printf("%s compare %s = %i\n", st1, st2, string_compare(st1, st2));
printf("%s compare %s = %i\n", st1, st1, string_compare(st1, st1));
printf("%s compare %s = %i\n", st2, st1, string_compare(st2, st1));
printf("%s compare %s = %i\n", st1, st3, string_compare(st1, st3));
}

執 行 結 果 :

abcde compare abdde = -1
abcde compare abcde = 0
abdde compare abcde = 1
abcde compare abcdefg = -1


說 明 :



i = 0;
while (st1[i] == st2[i] && st1[i] != '\0') i++;

由 指 數 0 開 始 比 較 , 直 至 出 現 不 相 同 字 元 或 st1[i] 是 結 尾 字 元 , 所 以 執 行 迴 圈 後 , 指 數 i 的 字 元 是 不 相 同 的 字 元 , 或 者 是 '\0' 。 為 什 麼 只 檢 查 st1[i] 而 不 用 檢 查 st2[i] 呢 ? 即 為 什 麼 不 寫 :


while (st1[i] == st2[i] && st1[i] != '\0' && st2[i] != '\0') i++;

因 為 如 果 st1[i] 等 於 st2[i] , 那 麼 st1[i] 不 等 於 什 麼 , 也 代 表 st[2] 不 等 於 什 麼 , 所 以 檢 查 st1 或 st2 也 沒 所 謂 , 不 必 兩 個 都 檢 查 了 。


if (st1[i] == st2[i]) {
return 0;
} else {
return (st1[i] > st2[i])? 1 : -1;
}

如 果 st1[i] 等 於 st2[i] , 就 表 示 兩 個 字 串 是 相 等 的 , 因 此 傳 回 0 , 這 時 候 , st1[i] 和 st2[i] 都 會 等 於 '\0' 。 如 果 它 們 不 相 等 , 就 跟 據 它 們 的 大 小 來 傳 回 1 或 -1 。


abcde compare abcdefg = -1

兩 個 不 同 長 度 的 字 串 也 沒 有 特 別 , 當 比 較 到 "abcdefg" 的 'f' 時 , 剛 好 到 了 另 一 字 串 的 '\0' :

a b c d e \0    
a b c d e f g \0

'f' 的 ASCII 碼 是 66h , '\0' 的 是 0 , 因 此 "abcde" 比 "abcdefg" 小 。


string_compare 函 數 好 像 Perl 的 cmp 運 算 子 。


實 例 : 基 本 字 串 處 理 : 字 串 加 法 上一節 下一節 到頁頂


兩 個 字 串 相 加 . 即 是 把 其 中 一 個 字 串 接 駁 到 另 一 字 串 的 尾 部 。

例 子 :

void string_concat(char st1[], char st2[], char result[]) {
int i, j;

i = 0;
while ( (result[i] = st1[i]) != '\0' ) i++;
j = i;
while ( (result[i] = st2[i - j]) != '\0' ) i++;
}

main() {
char st1[21] = "abcde", st2[21] = "012345", st3[21];

string_concat(st1, st2, st3);
printf("%s + %s = %s\n", st1, st2, st3);
}

執 行 結 果 :

abcde + 012345 = abcde012345


說 明 :



i = 0;
while ( (result[i] = st1[i]) != '\0' ) i++;

先 把 st1 複 製 到 result 。


j = i;
while ( (result[i] = st2[i - j]) != '\0' ) i++;

然 後 複 製 st2 。



實 例 : 基 本 字 串 處 理 : 字 串 位 置 上一節 下一節 到頁頂


尋 找 某 個 子 字 串 在 另 一 字 串 的 開 始 位 置 。

例 子 :

/* Insert "string_length" function here */

int index(char st[], char subst[]) {
int st_start, sti, substi, limit, result, st_length, subst_length;

result = -1;
st_length = string_length(st);
subst_length = string_length(subst);

limit = st_length - subst_length;
if (limit < st_start =" 0" sti =" st_start;" substi =" 0;" substi ="="" result =" st_start;" s =" %i\n" s =" %i\n" s =" %i\n" 012345 =" 0" 012345 =" 3" 012345 =" -1" result =" -1;" limit =" st_length" st_start =" 0" 3 =" limit" substi ="="" i =" 0;" st1 =" %s\n" 3 =" %s\n" 100 =" %s\n" 1 =" %s\n" 0 =" %s\n" st1 =" 012345" 3 =" 012" 100 =" 5" 1 =" Index" 0 =" 說" st_length =" string_length(st);" insert_length =" string_length(insert);" i =" st_length">= start ; i--) {
st[i + insert_length] = st[i];
}

for (i = 0 ; i < before =" %s\n" after =" %s\n" before =" 012345" after =" 01bcd2345" i =" st_length">= start ; i--) {
st[i + insert_length] = st[i];
}

搬 遷 位 置 start 或 之 後 的 字 元 , 包 括 '\0' , 以 便 把 insert 放 到 空 出 來 的 位 置 。

搬遷前 0 1 2 3 4 5 \0      
搬 遷 後 0 1 2 3 4 2 3 4 5 \0
插 入 後 0 1 b c d 2 3 4 5 \0

 


for (i = 0 ; i < insert_length ; i++) {
st[start + i] = insert[i];
}

插 入 insert 。


 


實 例 : 基 本 字 串 處 理 : 字 串 移 除 上一節 下一節 到頁頂


例 子 :

/* Insert "string_length" function here */

void string_remove(char st[], int start, int length) {
int i, limit;

if ( string_length(st) <= start ) return;

i = start + 1;
limit = start + length;
while (st[i] != '\0' && i < limit) i++;

while ( (st[i - length] = st[i]) != '\0' ) i++;
}

main() {
char st1[21] = "012345";

printf("st1 = %s\n", st1);
string_remove(st1, 2, 3);
printf("Index 2, length 3 = %s\n", st1);
}

執 行 結 果 :

st1 = 012345
Index 2, length 3 = 015


說 明 :



i = start + 1;
limit = start + length;
while (st[i] != '\0' && i < limit) i++;

去 到 要 移 除 的 字 串 的 後 一 個 字 元 , 即 i = limit = 5 , 或 去 到 '\0' 。 然 後 把 字 串 剩 餘 的 部 份 , 包 括 '\0' , 複 製 到 start 位 置 。

複製前 0 1 2 3 4 5 \0
複 製 後 0 1 5 \0 4 5 \0

 


while ( (st[i - length] = st[i]) != '\0' ) i++;

把 字 串 剩 餘 的 部 份 複 製 到 start 位 置 。


 


實 例 : 基 本 字 串 處 理 : 字 串 取 代 上一節 下一節 到頁頂


例 子 :

/* Insert "string_length" function here */
/* Insert "string_remove" function here */
/* Insert "string_insert" function here */

void string_replace(char st[], char out[], char in[]) {
int i, out_length;

i = index(st, out);
if (i != -1) {
out_length = string_length(out);
string_remove(st, i, out_length);
string_insert(st, in, i);
}
}

main() {
char st1[21] = "012345";

printf("Before, st1 = %s\n", st1);
string_replace(st1, "123", "OneTwoThree");
printf("After, st1 = %s\n", st1);
}

執 行 結 果 :

Before, st1 = 012345
After, st1 = 0OneTwoThree45


說 明 :



/* Insert "string_length" function here */
/* Insert "string_remove" function here */
/* Insert "string_insert" function here */

string_replace 函 數 會 用 到 string_length 、 string_remove 和 string_insert 函 數 , 其 運 作 原 理 是 : 找 尋 要 移 除 的 字 串 , 如 果 找 到 , 就 移 除 它 , 然 後 插 入 新 的 字 串 。


i = index(st, out);

找 尋 要 移 除 的 字 串 , 傳 回 來 的 子 字 串 位 置 i 會 用 在 string_remove 和 string_insert 函 數 。


if (i != -1)

如 果 找 到 的 話 。



實 例 : 字 典 上一節 下一節 到頁頂


本 節 介 紹 一 個 字 典 程 式 , 它 讓 你 輸 入 英 文 字 , 然 後 會 顯 示 它 的 解 釋 。 你 可 以 定 義 一 個 「 字 典 條 目 」 (Entry) 結 構 :


struct entry {
char word[11];
char definition[31];
}

一 個 條 目 包 括 英 文 字 word 和 解 釋 definition 。 假 設 這 個 字 典 有 5 個 條 目 , 你 可 以 用 結 構 陣 列 來 儲 存 這 個 字 典 :


struct entry dictionary[5];

這 樣 就 可 以 寫 :


dictionary[i].word
dictionary[i].definition

來 表 示 第 i 個 條 目 的 英 文 字 和 解 釋 。

你 可 以 寫 個 函 數 來 搜 尋 這 個 字 典 :


entry_index = lookup(dictionary, word, num_entry);

word 是 要 查 詢 的 英 文 字 , num_entry 是 條 目 數 目 , entry_index 是 word 在 dictionary 陣 列 的 位 置 。

例 子 :

struct entry {
char word[11];
char definition[51];
};

/* Insert "string_compare" function here */

int lookup(struct entry dictionary[], char word[], int num_entry) {
int i;

for (i = 0 ; i < num_entry ; i++) {
if ( string_compare(word, dictionary[i].word) == 0 ) return i;
}

return -1;
}

main() {
struct entry dictionary[5] = {
{"apple", "round fruit with firm juicy flesh"},
{"boy", "male child"},
{"cat", "small furry domesticated animal"},
{"dog", "common domestic animal kept by human"},
{"egg", "the cell from which the young is formed"},
};
char word[11];
int num_entry = 5, entry_index;

printf("Type '9' to quit.\n");
while (1) {
printf("Enter word: ");
scanf("%10s", word);

if (word[0] == '9') break;

entry_index = lookup(dictionary, word, num_entry);

if (entry_index != -1) {
printf("%s\n\n", dictionary[entry_index].definition);
} else {
printf("Sorry, the word is not in my dictionary.\n\n");
}
}
}

執 行 結 果 :

Type '9' to quit.
Enter word: apple
round fruit with firm juicy flesh

Enter word: egg
the cell from which the young is formed

Enter word: fork
Sorry, the word is not in my dictionary.

Enter word: 9


說 明 :


lookup 函 數 會 傳 回 查 詢 字 串 在 字 典 中 的 指 數 , 如 果 找 不 到 字 串 , 就 傳 回 -1 。



總 結 上一節 到頁頂


C 的 字 串 結 尾 字 元 是 '\0' , 使 用 結 尾 字 元 的 好 處 是 不 用 留 意 字 串 長 度 。

沒有留言: