由编程珠玑字符串倒置问题拓展到split之C\C++实现

一道字符串倒置的题目,不是全部倒置,而是每个单词倒置,单词之间顺序不变。

这道题目应该是很经典的,好多次看到,包含编程珠玑的案例,还有某博客写的百度一面面试题,典型的O(n)算法,每个word倒置,再整个string倒置。

这次要上战场了,却发现C C++的 字符串函数不熟练了,最近写的都是Java,一个Split函数爽到爆,于是决定要吧C C++的常用字符串函数过一遍,
并对各种方法实现,此处总结下Split。另外发现leetcode里面居然只支持new node(data),不支持常用的new node();因为之前习惯后者,可能leetcode都是写成类,而且头文件基本包括,总之提交的代码不能直接运用,后台会自动扩充成完整的,而且是面向对象的思想,我还用malloc去弄。

IT面试C C++ 是永恒的王道,尤其是字符串处理,必须信手拈来,函数的参数,返回值,功能,是否指针可写,C C++字符串转换必须熟记在心。

自己写的reverseWord,虽然AC掉了,但是代码极其丑陋,C C++字符串操作混乱,完全不统一。。。
void reverseWords(string &s) {
        
    string s_result="";
	char *s_dup=strdup(s.c_str());


	// first split,outside loop
	char *splitpoint=strtok(s_dup," ");

	while(splitpoint!=NULL)
	{
		//reverse each split word in place,int splitlength=strlen(splitpoint);
		for(int i=0;i<splitlength/2;i++)
		{
			char tmp=splitpoint[i];
			splitpoint[i]=splitpoint[splitlength-i-1];
			splitpoint[splitlength-i-1]=tmp;
		}

		//change char* to string,then each time concatenate to new string
		string reversesplitword=splitpoint;

		splitpoint=strtok(NULL," ");
		if(splitpoint!=NULL)
			s_result+=reversesplitword+" ";
		else
			s_result+=reversesplitword;
		
	}

	//reverse s_result;
	string tmp="";
	for(int i=0;i<s_result.length();i++)
		tmp=s_result[i]+tmp;
	s=tmp;
        
}


这里对Split功能的几种实现做一个总结,当时处理MCluster输入参数路径的时候,也用了这个函数,但是C C++字符串转换感觉很不爽。。。因为strtok源串必须为可修改的指针,string.c_str()是一个只读的char* 类型
之前习惯的都是C的那个函数strtok()
char *splitpoint=strtok(s_dup," ");
while(splitpoint!=NULL)
{
	splitpoint=strtok(NULL," ");
}

strtok函数有个非常奇怪的地方,就是源串居然会被修改,
" zhang rui chang "
执行一次,源串变为" zhang",splitpoint变为"zhang",因为他把zhang 后面分隔符自动填了\0,所以源字符串被截断了
但是他可以自动实现多个连续的分隔符split时全部都被删掉,恰好符合题目要求

这是C的split函数,由于string还是比较方便,直接转的时候c_str()不行,因此需要拷贝一个char* 出来很麻烦,也慢,于是考虑直接split string字符串
前几天和好基友伟哥聊了会儿,伟哥突然提到一个stringstream,我都差点忘了,当年C++教材里有一个这例子,专门处理那种ACM题目里一行的数据的个数是未知的情况,
但是我到后面一直没怎么用,没体会到他的好处。于是有了下面的代码,但是这个方法有个最大的局限,就是只能处理他设置好的delim,也即space \t 和\n这三种,

#include<stringstream>
...
str="  zhang rui   chang ";
istringstream istr(str);
while(istr>>splitstr)
{
	//process for each splitted str,splitstr;
}

如果修改delim的话,用getline函数改下就好了

www.haogongju.net/art/1554996

std::string token,text("Here,is,some:text"); //声明两个string token为空, text初始化为 "Here,some:text" 
std::stringstream iss(text); //声明一个stringstream类型 对象iss,并将其赋值给 text
while ( getline(iss,token,',') ) //从iss 读取字符串,并写入给字符串 token, 截断符使用 ',' 代替默认的截断符 '\n',直到读到iss的结尾位置为止 
{ 
	std::cout << token << std::endl; //输出每次截断的新内容 ...即 token的值 11 
}


String的实现,也可以考虑迭代器实现
blog.csdn.net/butterfly_dreaming/article/details/10142443
//注意:当字符串为空时,也会返回一个空字符串

void split(std::string& s,std::string& delim,std::vector< std::string >* ret)  
{  
    size_t last = 0;  
    size_t index=s.find_first_of(delim,last);  
    while (index!=std::string::npos)  
    {  
        ret->push_back(s.substr(last,index-last));  
        last=index+1;  
        index=s.find_first_of(delim,last);  
    }  
    if (index-last>0)  
    {  
        ret->push_back(s.substr(last,index-last));  
    }  
}
char* 也可以实现,但是和上面没本质区别

另外还有一种方法,就是用boost,但是这个感觉对我比较冷门,虽然有了这个,库就估计可以和Java比了,boost里面有专门的token处理

blog.diveinedu.net/%E4%B8%89%E7%A7%8D%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%86%E5%89%B2%E6%96%B9%E6%B3%95cc/
正如上面这位仁兄总结的三条主线差不多了。所以对于多数情况用string,就用stringstream吧,如果输入的char*,也可以先转string,或者直接用strtok哈~


指针传递的笔记:
指针传递是 传值,不是引用,因此创建一个链表最好返回一个指针值,而不要期待传递一个类似于引用传递的指针参数,因为穿进去的是一个拷贝了原指针指的指针变量

另外还有一个细节就是char string[ ]=”C++ programming language”;

用strlen 是24, 但是sizeof 25,原因是strlen是基于字符串本身的长度,当然不算结束符了,而sizeof是占得内存字节数,\0是占内存,切腹属于该字符串

char *a="C++ programming language";
cout<<strlen(a)<<endl<<sizeof(a)<<endl;

结果是:24和4,所以大家可以体会给char a[] 和char*a赋一个字符串的巨大区别了吧


但是如果 cout<<a<<endl<<(void*)a<<endl 前面输出的居然是字符串值,而不是一个地址值,而后面要转换成泛型指针才能输出地址值,小伙伴们都惊呆了,想起了C++书当时提到的C++智能处理。

再次感谢博主帮我回忆了C++字符串处理

www.weixueyuan.net/view/5851.html

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

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结