c语言中 字符串常量的界定符,C字符串操作函数
1.7.?分割字符串
很多文件格式或協議格式中會規定一些分隔符或者叫界定符(Delimiter),例如/etc/passwd文件中保存著系統的帳號信息:$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
...
每條記錄占一行,也就是說記錄之間的分隔符是換行符,每條記錄又由若干個字段組成,這些字段包括用戶名、密碼、用戶id、組id、個人信息、主目錄、登錄Shell,字段之間的分隔符是:號。解析這樣的字符串需要根據分隔符把字符串分割成幾段,C標準庫提供的strtok函數可以很方便地完成分割字符串的操作。tok是Token的縮寫,分割出來的每一段字符串稱為一個Token。#include char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
返回值:返回指向下一個Token的指針,如果沒有下一個Token了就返回NULL
參數str是待分割的字符串,delim是分隔符,可以指定一個或多個分隔符,strtok遇到其中任何一個分隔符就會分割字符串。看下面的例子。
例?25.2.?strtok
#include #include int main(void)
{
char str[] = "root:x::0:root:/root:/bin/bash:";
char *token;
token = strtok(str, ":");
printf("%s\n", token);
while ( (token = strtok(NULL, ":")) != NULL)
printf("%s\n", token);
return 0;
}$ ./a.out
root
x
0
root
/root
/bin/bash
結合這個例子,strtok的行為可以這樣理解:冒號是分隔符,把"root:x::0:root:/root:/bin/bash:"這個字符串分隔成"root"、"x"、""、"0"、"root"、"/root"、"/bin/bash"、""等幾個Token,但空字符串的Token被忽略。第一次調用要把字符串首地址傳給strtok的第一個參數,以后每次調用第一個參數只要傳NULL就可以了,strtok函數自己會記住上次處理到字符串的什么位置(顯然這是通過strtok函數中的一個靜態指針變量記住的)。
用gdb跟蹤這個程序,會發現str字符串被strtok不斷修改,每次調用strtok把str中的一個分隔符改成'\0',分割出一個小字符串,并返回這個小字符串的首地址。(gdb) start
Breakpoint 1 at 0x8048415: file main.c, line 5.
Starting program: /home/akaedu/a.out
main () at main.c:5
5{
(gdb) n
6char str[] = "root:x::0:root:/root:/bin/bash:";
(gdb)
9token = strtok(str, ":");
(gdb) display str
1: str = "root:x::0:root:/root:/bin/bash:"
(gdb) n
10printf("%s\n", token);
1: str = "root\000x::0:root:/root:/bin/bash:"
(gdb)
root
11while ( (token = strtok(NULL, ":")) != NULL)
1: str = "root\000x::0:root:/root:/bin/bash:"
(gdb)
12printf("%s\n", token);
1: str = "root\000x\000:0:root:/root:/bin/bash:"
(gdb)
x
11while ( (token = strtok(NULL, ":")) != NULL)
1: str = "root\000x\000:0:root:/root:/bin/bash:"
剛才提到在strtok函數中應該有一個靜態指針變量記住上次處理到字符串中的什么位置,所以不需要每次調用時都把字符串中的當前處理位置傳給strtok,但是在函數中使用靜態變量是不好的,以后會講到這樣的函數是不可重入的。strtok_r函數則不存在這個問題,它的內部沒有靜態變量,調用者需要自己分配一個指針變量來維護字符串中的當前處理位置,每次調用時把這個指針變量的地址傳給strtok_r的第三個參數,告訴strtok_r從哪里開始處理,strtok_r返回時再把新的處理位置寫回到這個指針變量中(這是一個Value-result參數)。strtok_r末尾的r就表示可重入(Reentrant),這個函數不屬于C標準庫,是在POSIX標準中定義的。關于strtok_r的用法Man Page上有一個很好的例子:
例?25.3.?strtok_r
#include #include #include int main(int argc, char *argv[])
{
char *str1, *str2, *token, *subtoken;
char *saveptr1, *saveptr2;
int j;
if (argc != 4) {
fprintf(stderr, "Usage: %s string delim subdelim\n",
argv[0]);
exit(EXIT_FAILURE);
}
for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
token = strtok_r(str1, argv[2], &saveptr1);
if (token == NULL)
break;
printf("%d: %s\n", j, token);
for (str2 = token; ; str2 = NULL) {
subtoken = strtok_r(str2, argv[3], &saveptr2);
if (subtoken == NULL)
break;
printf(" --> %s\n", subtoken);
}
}
exit(EXIT_SUCCESS);
}$ ./a.out 'a/bbb///cc;xxx:yyy:' ':;' '/'
1: a/bbb///cc
--> a
--> bbb
--> cc
2: xxx
--> xxx
3: yyy
--> yyy
a/bbb///cc;xxx:yyy:這個字符串有兩級分隔符,一級分隔符是:號或;號,把這個字符串分割成a/bbb///cc、xxx、yyy三個子串,二級分隔符是/,只有第一個子串中有二級分隔符,它被進一步分割成a、bbb、cc三個子串。由于strtok_r不使用靜態變量,而是要求調用者自己保存字符串的當前處理位置,所以這個例子可以在按一級分隔符分割整個字符串的過程中穿插著用二級分隔符分割其中的每個子串。建議讀者用gdb的display命令跟蹤argv[1]、saveptr1和saveptr2,以理解strtok_r函數的工作方式。
Man Page的BUGS部分指出了用strtok和strtok_r函數需要注意的問題:
這兩個函數要改寫字符串以達到分割的效果
這兩個函數不能用于常量字符串,因為試圖改寫.rodata段會產生段錯誤
在做了分割之后,字符串中的分隔符就被'\0'覆蓋了
strtok函數使用了靜態變量,它不是線程安全的,必要時應該用可重入的strtok_r函數,以后再詳細介紹“可重入”和“線程安全”這兩個概念
習題
1、出于練習的目的,strtok和strtok_r函數非常值得自己動手實現一遍,在這個過程中不僅可以更深刻地理解這兩個函數的工作原理,也為以后理解“可重入”和“線程安全”這兩個重要概念打下基礎。
2、解析URL中的路徑和查詢字符串。動態網頁的URL末尾通常帶有查詢,例如:
比如上面第一個例子,是路徑部分,?號后面的complete=1&hl=zh-CN&ie=GB2312&q=linux&meta=是查詢字符串,由五個“key=value”形式的鍵值對(Key-value Pair)組成,以&隔開,有些鍵對應的值可能是空字符串,比如這個例子中的鍵meta。
現在要求實現一個函數,傳入一個帶查詢字符串的URL,首先檢查輸入格式的合法性,然后對URL進行切分,將路徑部分和各鍵值對分別傳出,請仔細設計函數接口以便傳出這些字符串。如果函數中有動態分配內存的操作,還要另外實現一個釋放內存的函數。完成之后,為自己設計的函數寫一個Man Page。
總結
以上是生活随笔為你收集整理的c语言中 字符串常量的界定符,C字符串操作函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用c语言实现存储和读取图片文件,C++实
- 下一篇: c语言订餐管理系统报告,用c语言编程小型