函数原型:
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 ";因为这是常量字符串,不能修改。
C 库函数 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
标签:trim,123456,char,strsep,str,include,buf,isspace From: https://www.cnblogs.com/rebrobot/p/16977963.html