1. 程式人生 > 其它 >linux c 語言 strsep trim isspace

linux c 語言 strsep trim isspace

函式原型:

                            char *strtok(char *s, const char *delim);

                            char *strsep(char **s, const char *delim);

功能:strtok和strsep兩個函式的功能都是用來分解字串為一組字串。s為要分解的字串,delim為分隔符字串。

       返回值:從s開頭開始的一個個子串,當沒有分割的子串時返回NULL。

       相同點:兩者都會改變源字串,想要避免,可以使用strdupa(由allocate函式實現)或strdup(由malloc函式實現)。

strtok函式第一次呼叫時會把s字串中所有在delim中出現的字元替換為NULL。然後通過依次呼叫strtok(NULL, delim)得到各部分子串。

作用:
        分解字串為一組字串。s為要分解的字串,delim為分隔符字串。
說明:
        strtok()用來將字串分割成一個個片段。引數s指向欲分割的字串,引數delim則為分割字串,當strtok()在引數s的字串中發現到引數delim的分割字元時則會將該字元改為\0 字元。在第一次呼叫時,strtok()必需給予引數s字串,往後的呼叫則將引數s設定成NULL。每次呼叫成功則返回下一個分割後的字串指標。
返回值:
      從s開頭開始的一個個被分割的串。當沒有被分割的串時則返回NULL。
      所有delim中包含的字元都會被濾掉,並將被濾掉的地方設為一處分割的節點。(如下面的例子,可修改 seps裡面的資料,然後看輸出結果)

#include <string.h>
#include <stdio.h>
char string[] ="A string\tof ,,tokens\nand some  more tokens";
char seps[]   =" ,\t\n";
char *token;
int main( void )
{
   printf( "%s\n\nTokens:\n", string );
/* Establish string and get the first token: */
   token = strtok( string, seps );
while( token != NULL )
   {
/* While there are tokens in "string" */
      printf( " %s\n", token );
/* Get next token: */
      token = strtok( NULL, seps );
   }
return 0;
}

總結:

strtok內部記錄上次呼叫字串的位置,所以不支援多執行緒,可重入版本為strtok_r,有興趣的可以研究一下。它適用於分割關鍵字在字串之間“單獨”或是 “連續“在一起的情況。

strsep:

#include <string.h>
#include <stdio.h>
char string[] ="A string\tof ,,tokens\nand some  more tokens";
char seps[]   =" ,\t\n";
char *token, *s;
int main( void )
{
   printf( "%s\n\nTokens:\n", string );
/* Establish string and get the first token: */
   s=string;
   token = strsep( &s, seps );
while( token != NULL )
   {
/* While there are tokens in "string" */
      printf( " %s\n", token );
/* Get next token: */
      token = strsep( &s, seps );
   }
return 0;
}

為什麼用strtok時子串中間沒有出現換行,而strsep卻有多個換行呢?文件中有如下的解釋:

One difference between strsep and strtok_r is that if the input string contains more
than one character from delimiter in a row strsep returns an empty string for each
pair of characters from delimiter. This means that a program normally should test
for strsep returning an empty string before processing it.

    大意是:如果輸入的串的有連續的多個字元屬於delim,(此例source中的逗號+空格,感嘆號+空格等就是這種情況),strtok會返回NULL而strsep會返回空串  ""。因而我們如果想用strsep函式分割字串必須進行返回值是否是空串的判斷。這也就解釋了strsep的例子中有多個換行的原因。

改進後的程式碼:

效果:

其中,  字元‘\0’  的 10進位制數為0 , 巨集定義為  NULL  。 

下面的說明摘自於最新的Linux核心2.6.29,說明了strtok()已經不再使用,由速度更快的strsep()代替。

/** linux/lib/string.c** Copyright (C) 1991, 1992 Linus Torvalds*/  

/** stupid library routines.. The optimized versions should generally be found  

* as inline code in <asm-xx/string.h>  

* These are buggy as well..  

* * Fri Jun 25 1999, Ingo Oeser <[email protected]>  

* - Added strsep() which will replace strtok() soon (because strsep() is  

* reentrant and should be faster). Use only strsep() in new code, please.  

** * Sat Feb 09 2002, Jason Thomas <[email protected]>,  

* Matthew Hawkins <[email protected]>  

* - Kissed strtok() goodbye

*/

總結:

strsep返回值為分割後的開始字串,並將函式的第一個引數指標指向分割後的剩餘字串。它適用於分割關鍵字在兩個字串之間只嚴格出現一次的情況。

PS:

因為函式內部會修改原字串變數,所以傳入的引數不能是不可變字串(即文字常量區)。

如 char *tokenremain ="abcdefghij"//編譯時為文字常量,不可修改。

strtok(tokenremain,"cde");

strsep(&tokenremain,"cde");

編譯通過,執行時會報段錯誤

來自:

https://www.cnblogs.com/devilmaycry812839668/p/6353912.html

 ==========================================================

https://www.cnblogs.com/coolYuan/p/14373174.html

今天在呼叫strsep函式時,報了Segmentation fault錯誤,strsep函式原型如下:

char *strsep(char **stringp, const char *delim);

第一個引數是個二級指標,而且沒有const修飾,我猜測在man手冊中只要是新增const修飾的引數都是隻讀的(當然這是肯定的),而那些沒有新增const修飾的一般為可讀可寫的。

根據這種猜測,再根據呼叫strsep時。傳遞的第一個引數是一個const char *,這時程式執行時就回報Segmentation fault錯誤。

例:

 1 #include <stdio.h>
 2 
 3 int main(void)
 4 {
 5     char *buf = "123456,123456";
 6 
 7     strsep(&buf, ",");
 8 
 9     return 0;
10 }

編譯執行結果:

# ./a.out 
Segmentation fault

修改如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 int main(void)
 6 {
 7     char *buf = malloc(32);
 8 
 9     memcpy(buf, "123456,123456", sizeof("123456,123456"));
10 
11     strsep(&buf, ",");
12 
13     return 0;
14 }

另一種修改方式:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 int main(void)
 6 {
 7     char buf[32] = {0};
 8     
 9     memcpy(buf, "123456,123456", sizeof("123456,123456"));
10 
11     strsep(&buf, ",");
12 
13     return 0;
14 }

上邊這種修改在編譯的時候會有一個警告:

4.c: In function ‘main’:
4.c:13:5: warning: passing argument 1 of ‘strsep’ from incompatible pointer type [enabled by default]
     strsep(&buf, ",");
     ^
In file included from 4.c:3:0:
/usr/include/string.h:555:14: note: expected ‘char ** __restrict__’ but argument is of type ‘char (*)[32]’
 extern char *strsep (char **__restrict __stringp,
              ^

可以將strsep(&buf, ",");修改為strsep((char **)(&buf), ",");以消除影響。但是這個並不是導致問題的所在。

上邊的修改並沒有解決問題。

再次修改:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 int main(void)
 6 {
 7     char buf[32] = {0};
 8     char *tmp = NULL;
 9     
10     memcpy(buf, "123456,123456", sizeof("123456,123456"));
11 
12     tmp = buf;
13     strsep(&tmp, ",");
14 
15     return 0;
16 }

以上兩種方式都可以解決所遇到的問題。

這是什麼原因的導致的呢!以下是從glibc中找到的原始碼:

 1 char *
 2 __strsep (char **stringp, const char *delim)
 3 {
 4   char *begin;
 5 
 6   assert (delim[0] != '\0');
 7 
 8   begin = *stringp;
 9   if (begin != NULL)
10     {
11       char *end = begin;
12 
13       while (*end != '\0' || (end = NULL))
14     {
15       const char *dp = delim;
16 
17       do
18         if (*dp == *end)
19           break;
20       while (*++dp != '\0');
21 
22       if (*dp != '\0')
23         {
24           *end++ = '\0';
25           break;
26         }
27 
28       ++end;
29     }
30 
31       *stringp = end;
32     }
33 
34   return begin;
35 }
 1 char *
 2 __strsep (char **stringp, const char *delim)
 3 {
 4   char *begin, *end;
 5 
 6   begin = *stringp;
 7   if (begin == NULL)
 8     return NULL;
 9 
10   /* Find the end of the token.  */
11   end = begin + strcspn (begin, delim);
12 
13   if (*end)
14     {
15       /* Terminate the token and set *STRINGP past NUL character.  */
16       *end++ = '\0';
17       *stringp = end;
18     }
19   else
20     /* No more delimiters; this is the last token.  */
21     *stringp = NULL;
22 
23   return begin;
24 }

以上是在glic中找到的倆種實現方法,這兩種方式都對stringp中的資料進行了修改,所以才會出現上述錯誤。

在解決問題的過程中,還遇見一個問題:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 int main(void)
 6 {
 7     char *buf = malloc(32);
 8     
 9     buf = "123456,123456";
10 
11     memcpy(buf, "123456,123456", sizeof("123456,123456"));
12 
13     strsep(&buf, ",");
14 
15     return 0;
16 }

上邊這段程式碼在執行的時候也會報Segmentation fault錯誤,這其實是因為buf的指向從堆區換到了只讀儲存區,所以會出現錯誤。這個錯誤很容易發現,但是在編碼的過程中不注意還是很容易犯的,可能是我自身的問題,以作記錄,以謹記。

==========================================================

c語言實現的trim函式,去掉字串兩端的空白字元

#include <string.h> #include <stdio.h> #include <ctype.h>   char *trim(char *str) {         char *p = str;         char *p1;         if(p)         {             p1 = p + strlen(str) - 1;             while(*p && isspace(*p))                  p++;             while(p1 > p && isspace(*p1))                  *p1--=0;         }         return p; }   //去除尾部空格 char *rtrim(char *str) {     if(str == NULL||*str == '\0')     {         return str;     }     int len = strlen(str);     char *p=str+len-1;     while(isspace(*p)&&p>=str)     {         *p = '\0';          --p;     }     return str; }   char *ltrim(char *str) {       if(str == NULL||*str == '\0')     {         return str;     }     char *p = str;     int len = 0;     while(*p != NULL&&isspace(*p))     {         ++p;         ++len;     }     memmove(str,p,strlen(str)-len+1);     return str; }   int main() {       char a[]="   asa   ";     char* h=trim(a);     printf("%s\n",h);     return 0; }

ps:不能直接用char* a="    asd   ";因為這是常量字串,不能修改。

 

庫函式 int isspace(int c)檢查所傳的字元是否是空白字元。

標準的空白字元包括:

1 2 3 4 5 6 ' '     (0x20)    space (SPC) 空格符 '\t'    (0x09)    horizontal tab (TAB) 水平製表符     '\n'    (0x0a)    newline (LF) 換行符 '\v'    (0x0b)    vertical tab (VT) 垂直製表符 '\f'    (0x0c)    feed (FF) 換頁符 '\r'    (0x0d)    carriage return (CR) 回車符

轉自:https://www.cnblogs.com/fightformylife/p/4361148.html

https://www.cnblogs.com/jeffhl/p/9881785.html

 

參考:

https://www.cnblogs.com/coolYuan/p/14373174.html

https://www.cnblogs.com/devilmaycry812839668/p/6353912.html