【C/C++ 字符串】探索C语言之字符串分割函数:strtok和strsep的区别

简介: 【C/C++ 字符串】探索C语言之字符串分割函数:strtok和strsep的区别

概述

在c语言中,字符串分割函数主要有两种:一是strtok函数,另一个就是strsep函数。
下面我们对这两个函数作一个详细解释说明。

对比项 strtok strsep
功能 根据一组分隔符对字符串进行分割 根据一组分隔符对字符串进行分割
是否修改原始字符串
线程安全性 不是线程安全的 是线程安全的
处理相邻分隔符的方式 如果两个分隔符相邻,它会将它们视为一个 如果两个分隔符相邻,它会将它们视为两个,并在它们之间返回一个空字符串
返回值 返回指向分隔符之前的第一个字符的指针,同时将分隔符替换为 ‘\0’ 返回指向分隔符之前的第一个字符的指针,同时将分隔符替换为 ‘\0’
用途 用于解析简单的、格式固定的字符串,如 CSV 文件 用于解析复杂的、格式可变的字符串,如 HTTP 请求头

strsep - extract token from string(linux 下)

#include <string.h>
char *strsep(char **s, const char *delim);
//会修改数据源。可重入的,注意这里虽然改动stringp的内容,主要是不在使用static静态变量了。

作用:
stringp为指向欲分割的字符串,delim为分隔符,函数将返回分隔符前面的字符串,stringp将指向分隔符之后的字符串。
Be cautious when using this function. If you do use it, note that:
This function modifies its first argument.
This function cannot be used on constant strings.
The identity of the delimiting character is lost.


strtok, strtok_r - extract tokens from strings

#include <string.h>
char *strtok(char *str, const char *delim);
//会修改数据源。外部加锁才线程安全(strtok执行结束再解锁执行另一个strtok循环知道工作完成) char
*strtok_r(char *str, const char *delim, char **saveptr);

代码示例:*

#include <string.h>
#include <stdio.h> int main(int arg, const char *argv[]) {
  /*字符串不能为常量*/ 
  char* string = strdup("/home/yinlijun/project:/home/yinlijun:/home/someone");
  char* p;
   while((p = strsep(&string, ":")) != NULL) /*第一个参数设为二级指针,字符串中所有的第二个参数(子串)最后会被替代成‘/0’*/ 
     { 
       printf("%s/n", p);
     }
   free(string); return 0; }

相同点

对该函数的调用序列将str拆分为token,这些token是由作为定界符一部分的任何字符分隔的连续字符的序列。
首次调用时,该函数需要一个C字符串作为str的参数,该字符串的第一个字符用作扫描令牌的起始位置。
在随后的调用中,该函数需要一个空指针,并使用最后一个标记结束后的位置作为扫描的新起始位置。
为了确定token的开头和结尾,该函数首先从起始位置扫描未包含在定界符中的第一个字符(该字符成为token的开头)。
然后从token的开头开始扫描定界符中包含的第一个字符,该字符成为token的末尾。如果找到终止的空字符,扫描也会停止。
token的此结尾将自动替换为空字符(NULL),并且token的开头由函数返回。
一旦在对strtok的调用中找到了str的终止null字符,此函数的所有后续调用(将null指针作为第一个参数)都将返回null指针。
找到下一个标记的点由在下一次调用时使用的函数在内部保留(不需要特殊的库实现来避免数据争用)。
两者都会对原字符串进行修改。


不同点

  • strtok内部记录上次调用字符串的位置,所以是不可重入的,不支持多线的,其可重入版本为strtok_r。
  • strsep使用传入的参数来确定字符串的起始位置,是可重入的,也是Linux kernel推荐的函数,strtok的替代品。
  • strtok()是被标准化(C标准,并因此也通过POSIX),
    但strsep()不规范(由C或POSIX;它是GNU C库中可用的,和起源于BSD).因此,可移植的代码strtok()比strsep().更容易使用
  • strtok()允许在单个标记之间使用多个分隔符,而strsep()期望标记之间使用单个分隔符,并将相邻分隔符解释为空标记.

什么时候使用它们?

  • 使用strsep函数,当您需要处理空字段,或者在不同字段之间使用单个分隔符,并且在不介意可移植性的情况下。
  • 使用strtok_r函数,当您希望在字段之间允许多个分隔符,不需要处理空字段,并且要求代码具有良好的POSIX兼容性。
  • 尽量避免使用strtok函数,因为它是不可重入的,容易导致问题。尤其在多线程环境中,strtok的使用可能导致程序出现不稳定的行为。

您可以strsep()在需要空字段时使用,而不是在字段之间允许多个分隔符,并且在您不介意可移植性时使用.
你会使用strtok_r(),当你想允许标记之间的多个分隔符,你不想空字符(和POSIX足够的便携式你).
strtok()如果你不这样做,你只会在有人威胁你的生命时使用.而你只能用它足够长的时间让你摆脱危及生命的境地; 然后你会再次放弃使用它.它有毒; 不要使用它.编写自己的strtok_r()或者strsep()使用它会更好strtok().

为什么strtok()有毒?

strtok()如果在库函数中使用,该函数是有毒的.如果您的库函数使用strtok(),则必须清楚地记录.
那是因为:

  • 如果任何调用函数正在使用strtok()并调用也使用的函数strtok(),则会中断调用函数.
  • 如果你的函数调用任何调用的函数strtok(),那将破坏你的函数的使用strtok().
  • 如果您的程序是多线程的,那么strtok()在任何给定时间最多只能有一个线程使用- 跨越一系列strtok()调用.

此问题的根源是调用之间保存的状态,允许strtok()在停止的位置继续.除了"不要使用strtok()" 之外,没有合理的方法来解决问题.

  • strsep()如果可用,您可以使用.
  • 您可以使用POSIX(strtok_r()如果可用).
  • strtok_s()如果可用,您可以使用Microsoft .
  • 名义上,您可以使用ISO/IEC 9899:2011附件K.3.7.3.1功能strtok_s(),但其界面strtok_r()与微软的界面不同strtok_s()


目录
相关文章
|
18天前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
29 0
|
3天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)
|
17天前
|
C语言 C++ 数据格式
【C++对于C语言的扩充】C++与C语言的联系,命名空间、C++中的输入输出以及缺省参数
【C++对于C语言的扩充】C++与C语言的联系,命名空间、C++中的输入输出以及缺省参数
|
2天前
|
C语言
【C语言】字符分类函数与字符转换函数
【C语言】字符分类函数与字符转换函数
7 1
|
2天前
|
程序员 编译器 C语言
C语言之函数与参数
C语言之函数与参数
5 0
|
3天前
|
C语言
C语言:字符函数和字符串函数(strlen strcat strcmp strncmp等函数和模拟实现)
C语言:字符函数和字符串函数(strlen strcat strcmp strncmp等函数和模拟实现)
|
5天前
|
存储 C语言
C语言函数的返回值
C语言函数的返回值
7 0
|
5天前
|
C语言 Windows
C语言中的fopen与fclose函数详解
C语言中的fopen与fclose函数详解
11 1
|
5天前
|
C语言
深入理解C语言中的printf函数及数据输出
深入理解C语言中的printf函数及数据输出
13 0
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”