C++实现字符串分割函数split()「建议收藏」

C++实现字符串分割函数split()「建议收藏」目录使用strtok()完成分割使用strsep()完成分割使用strtok_r()完成分割实现字符串分割前言最近遇到了一个字符串分割的问题,在C++的标准库里面没有字符分割函数split()。我想这个问题今后可能还会再遇见,所以使用C/C++中的字符串分割方法完成相应的功能。需求:对一个字符串进行分割,然后将分割后的数据存起来,想用的时候像用数组那样拿就行了。所以在完…

大家好,又见面了,我是你们的朋友全栈君。

目录

使用strtok()完成分割

使用strsep()完成分割

使用strtok_r()完成分割

实现字符串分割


前言

最近遇到了一个字符串分割的问题,在C++标准库里面没有字符分割函数split()。我想这个问题今后可能还会再遇见,所以使用C/C++中的字符串分割方法完成相应的功能。

需求:对一个字符串进行分割,然后将分割后的数据存起来,想用的时候像用数组那样拿就行了。所以在完成分割后,需要使用vector将相应的数据保存起来。

 

使用strtok()完成分割

char * strtok ( char * str,const char * delim );
其中str是需要分割的字符串。delim是分隔符标识。
在<cstring>  <string.h>中定义

该函数被设计为多次调用,以从同一字符串中获取连续的token。

如果 str != NULL,该调用被视为 strtok 对此特定字符串的第一个调用。该函数搜索中不包含的第一个字符 delim。

        如果找不到这样的字符,则根本没有标记str,并且该函数返回空指针。

        如果找到了这样的字符,则这是token的开头。然后,函数从该点开始搜索包含delim的第一个字符。

                如果未找到这样的字符,str则只有一个标记,以后调用strtok将返回一个空指针

                如果找到了这样的字符,则将其替换为空字符’\0’,并将指向下一个字符的指针存储在静态位置中,以供后续调用。

        然后,该函数将指针返回到token的开头

如果str == NULL,则该调用将被视为对后续调用strtok:该函数从上次调用中保留的位置继续。行为与先前存储的指针作为str传递的行为相同。

#include <cstring>
#include <iostream>
 
int main() 
{
    char input[100] = "A, bird, came, down, the, walk";
    char *token = std::strtok(input, ", ");
    while (token != NULL) {
        std::cout << token << '\n';
        token = std::strtok(NULL, ", ");
    }
}

C++实现字符串分割函数split()「建议收藏」

笔记:

(1)此函数具有破坏性:它将 ‘\0’ 字符写入 str 字符串的元素中。

(2)每次对该函数的调用都会修改一个静态变量:不是线程安全的。

(3)与大多数其他strtok不同,每个后续标记中的定界符可以不同,甚至可以取决于先前标记的内容。

(4)其中delim可以包含多个分隔符,strtok函数会将str内容按分隔符分割。如果对数据格式有要求,需要注意这一点。

 

源自百度百科:

下面的说明摘自于Linux内核2.6.29,说明了这个函数已经不再使用,由速度更快的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 <ioe@informatik.tu-chemnitz.de>
* – 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 <jason@topic.com.au>,
* Matthew Hawkins <matt@mh.dropbear.id.au>
* – Kissed strtok() goodbye
*/

 

使用strsep()完成分割

strsep函数用于分解字符串为一组字符串。其原型为:

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

 

存在的问题

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//char *strsep(char **str, const char *delim); 
int main(void) {
	char source[] = "hello, world! welcome to china!";
	char delim[] = " ,!";
	char *input = strdup(source);
	char *token = strsep(&input, delim);
		while (token != NULL) {
			printf(token);
			printf("-");
			token = strsep(&input, delim);
                        printf("\n");
		}
	free(input);
	
	return 0;
}

C++实现字符串分割函数split()「建议收藏」

为什么使用strsep()分割字符串后会多出几个” – ” ?

原因是:如果输入的串的有连续的多个字符属于delim,strtok会返回NULL,而strsep会返回空串 “”。如果想用strsep函数分割字符串必须进行返回值是否是空串的判断。

 

使用strtok_r()完成分割

strtok是一个线程不安全的函数,因为它使用了静态分配的空间来存储被分割的字符串位置,线程安全的函数是strtok_r()。

char *strtok_r(char *str, const char *delim, char **saveptr);

strtok_r函数是strtok函数的可重入版本。str为要分解的字符串,delim为分隔符字符串。char **saveptr参数是一个指向char *的指针变量,用来在strtok_r内部保存切分时的上下文,以应对连续调用分解相同源字符串。

第一次调用strtok_r时,str参数必须指向待提取的字符串,saveptr参数的值可以忽略。连续调用时,str赋值为NULL,saveptr为上次调用后返回的值,不要修改。一系列不同的字符串可能会同时连续调用strtok_r进行提取,要为不同的调用传递不同的saveptr参数。

strtok_r实际上就是将strtok内部隐式保存的this指针,以参数的形式与函数外部进行交互。由调用者进行传递、保存甚至是修改。需要调用者在连续切分相同源字符串时,除了将str参数赋值为NULL,还要传递上次切分时保存下的saveptr。

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
 
//char *strtok_r(char *str, const char *delim, char **saveptr);
int main()
{
	char *Src = "Can I help you";
	char Buffer[100];
	char *delim = " ";
	char *saveptr = NULL;
	char *input = NULL;
	strncpy(Buffer, Src, sizeof(Buffer) - 1);
	input = Buffer;
	while(NULL != ( input = strtok_r( input, delim, &saveptr)))
	{
		printf("input[%s]   saveptr[%s]\n", input,saveptr);
		input = NULL;
	}
	return 0;
}

C++实现字符串分割函数split()「建议收藏」

 

实现字符串分割

以上函数都会改变源字符串,所以在完成split函数功能时,要先复制一个副本,对副本进行分割后返回相应的值才是正确的思路。

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

namespace tyler
{
//strtok版本
//char *strtok(char s[], const char *delim);
	vector <string> stringsplit(const string &str, const char *delim)
	{
		vector <std::string> strlist;
		int size = str.size();
		char *input = new char[size+1];
		strcpy(input, str.c_str());
		char *token = std::strtok(input, delim);
		while (token != NULL) {
			strlist.push_back(token);
			token = std::strtok(NULL, delim);
		}
		delete []input;
		return strlist;
	}
	
//另一种写法
//for(token = strtok(input, delim); token != NULL; token = strtok(NULL, delim)) 
//{
//}

//strsep版本
//char *strsep(char **stringp, const char *delim);
	vector <string> stringsplit1(const string &str, const char *delim)
	{
		vector <std::string> strlist;
		char *p = const_cast<char*>(str.c_str());
		char *input = strdup(p);       //strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。
		char *token = strsep(&input, delim);
		while (token != NULL) {
			if(strcmp(token, "") == 0)
				token = strsep(&input, delim);
			else
			{
				strlist.push_back(token);
				token = strsep(&input, delim);
			}
		}
		free(input);
		return strlist;
	}

//strtok_r版本
//char *strtok_r(char *str, const char *delim, char **saveptr);
	vector <string> stringsplit2(const string &str, const char *delim)
	{
		vector <std::string> strlist;
		char *saveptr = NULL;
		char *p = const_cast<char*>(str.c_str());
		char *input = strdup(p);       //strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。
		while(NULL != ( input = strtok_r( input, delim, &saveptr) ))
		{
			//printf("input[%s] saveptr[%s]\n",input,saveptr);
			strlist.push_back(input);
			input = NULL;
		}
		free(input);
		return strlist;
	}
}

int main()
{
	string str = "hello, world! welcome to china!";
	cout << str <<endl;
	vector<string> asd = tyler::stringsplit2(str, " ,!");
	cout << "StringList:";
	for(int i = 0; i < asd.size(); ++i)
	{
		cout << "[" << asd[i] << "]";
	}
	cout <<endl;
	cout << "String:" << str <<endl;
	return 0;
}

C++实现字符串分割函数split()「建议收藏」

C++实现字符串分割函数split()「建议收藏」

 

参考:

https://baike.baidu.com/item/strtok_r

https://blog.csdn.net/yafeng_jiang/article/details/7109285

https://www.cnblogs.com/carsonzhu/p/5859552.html

https://blog.csdn.net/zhouzhenhe2008/article/details/74011399

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/136649.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • TCP的几个状态

    TCP的几个状态

  • phpstorm2021.11 激活【2021免费激活】「建议收藏」

    (phpstorm2021.11 激活)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.html0E14HXZ4QL-eyJsaWN…

  • lm opencv 算法_Levenberg–Marquardt算法学习(和matlab的LM算法对比)[通俗易懂]

    lm opencv 算法_Levenberg–Marquardt算法学习(和matlab的LM算法对比)[通俗易懂]回顾高斯牛顿算法,引入LM算法惩罚因子的计算(迭代步子的计算)完整的算法流程及代码样例1.回顾高斯牛顿,引入LM算法根据之前的博文:Gauss-Newton算法学习假设我们研究如下形式的非线性最小二乘问题:r(x)为某个问题的残差residual,是关于x的非线性函数。我们知道高斯牛顿法的迭代公式:Levenberg–Marquardt算法是对高斯牛顿的改进,在迭代步长上略有不同:最…

  • springbean生命周期通俗一点_spring为啥是单例模式

    springbean生命周期通俗一点_spring为啥是单例模式一、Spring核心模块介绍1.SpringCore:Core封装包是框架的最基础部分,提供IOC和依赖注入特性。这里的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。2.SpringContext:构建于Core封装包基础上的Context封装包,提供了一种框架式的对象访问方法,有些象JNDI注册器。Context封装包的特性得自于Beans封装包,并添加了对国际化(I18N).

  • python常用面试题_Python+Selenium 常见面试题整理[通俗易懂]

    python常用面试题_Python+Selenium 常见面试题整理[通俗易懂]整理加复习1、记录一下刚刚看到的博客https://www..com/lesleysbw/p/5916022.html中的一小段非常认同看到一位大神的面试准备,我不想写了TT。等我好好看完,再决定怎么继续写下去吧,照搬毫无意义。大神博客地址:https://www..com/lesleysbw/category/946223.htmlQunar机票搜索场景访问Qunar机票首页http://fli…

  • [转载]Matlab fmincon函数用法

    [转载]Matlab fmincon函数用法原文地址:fmincon函数用法”style=”text-decoration:none;color:rgb(151,38,38)”>Matlab fmincon函数用法作者:长笛人倚楼Gloria这个函数在之前优化工具箱一文中已经介绍过,由于其应用广泛,所以这里通过实例单独整理一下其用法。一、基本介绍求解问题的标准型为minF(X)s.tAXAeqX=

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号