C++字符串解析分享,求优化

时间:2022-03-29 06:00:04
需求:字符串“#C255 244 233 12#   #d4#       #f5 ,将其中的数字和字母分离出来,其中‘#’字符可能是别的字符

我自己写的比较繁琐,使用了C++的string类和VECTOR,当然使用c可能更高效一些,分享一下,求指教,如何才能写的更简洁。
 
 
 #include "iostream"
 #include "string"
 #include "vector"
 
 using namespace std;
 //using namespace string;
 
 
 const string const_str = "#C255 244 233 22##H5##a1#";
 
 //检测##的位置以及数目 
 vector<int> GetTagPos(const string& str ,const string& tag)
 {
  vector<int> iPosList;
 
size_t tag_pos = 0 ;
size_t search_pos = 0;
size_t search_length = str.length();

  iPosList.clear();
  iPosList.reserve(32);
  if(tag.empty()  || str.empty())
  goto _return;

while(search_pos <= search_length)   //检测tag位置 
{
if((tag_pos = str.find(tag,search_pos))!= string::npos )
{
iPosList.push_back(tag_pos);
search_pos = tag_pos + 1 ;
}
else
{
break; 

}

if(iPosList.size()%2 != 0 )   //如果不配对,返回空 
{
iPosList.clear();
goto _return;
}

_return:
return iPosList ;
 }
 
 
 void GetResult(const string& str , const string& tag )
 {
  size_t ipos = 0 ;
size_t word_pos = 0 ;
string strNum;
vector<int> itagpos;
 
if(true == str.empty())
return ;

itagpos = GetTagPos(str,tag);
if(itagpos.empty())
return ;

vector<int>::iterator it = itagpos.begin(); 
for( ; it != itagpos.end() ; ++ it)    //首先检测字母 
{
for(ipos = (*it) + 1 ; ipos != *(++it)+1 ; ++ipos)
{
char c = str[ipos] ;
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
{
cout << c << endl ;
word_pos = ipos ;
break;
}
else
{
continue ;
}
}

strNum.clear();
strNum.erase();
for(ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字 
{
char c = str[ipos];
if( c >= '0'  && c <= '9')
{
strNum += c ;
}
else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
{
cout << strNum << "\n"<< endl;   // 输出了字符,可以转换为数字 
strNum.clear();
continue ;
}
else
{
continue ;
}
}

 } 
 
 int main()
 {
  cout << "string parse test\n"<< endl;
 
  string tag = "#" ;
  GetResult(const_str,tag);
 
  return  0 ;
 }

54 个解决方案

#1


我没有检查你的程式正确与否,只是做了一些小小的改动


#include <iostream>
#include <string>
#include <vector>

//const std::string const_str = "#C255 244 233 22##H5##a1#"; //try your best to avoid global parameters, most of the times global params are evils

//检测##的位置以及数目
std::vector<int> GetTagPos(const std::string& str ,const std::string& tag)
{
    //check the tag and str are empty or not first, this is one of the technique which called "lazy evaluation"
    if(tag.empty()  || str.empty()){
        return std::vector<int>();
    }

    std::vector<int> iPosList;
    size_t tag_pos = 0 ;
    size_t search_pos = 0;
    size_t search_length = str.length();

    //iPosList.clear(); //don't need to clear it since it do not have any data at all
    iPosList.reserve(32);
    while(search_pos <= search_length)   //检测tag位置
    {
        if((tag_pos = str.find(tag,search_pos)) != std::string::npos )
        {
            iPosList.push_back(tag_pos);
            search_pos = tag_pos + 1 ;
        }
        else
        {
            break;
        }
    }

    if(iPosList.size() % 2 != 0 )   //如果不配对,返回空
    {
        return std::vector<int>();
    }

    return iPosList; //remove goto, this is c++, not c, with the helps of RAII,we rarely need goto
}


void GetResult(const std::string& str , const std::string& tag )
{
     //check the str is empty or not first, this is one of the technique which called "lazy evaluation"
    if(true == str.empty()){
        return;
    }

    std::vector<int> itagpos = GetTagPos(str, tag); //this way the codes are shorter and compiler has more chances to optimize your codes
    if(itagpos.empty()){
        return ;
    }

    //size_t ipos = 0 ; //don't need to declare your local paorameter at here, you could declare it later on
    size_t word_pos = 0 ;
    std::string strNum;
    auto end = std::end(itagpos); //you could use auto to save you some typing trouble
    for(auto it = std::begin(itagpos); it != end ; ++it)    //首先检测字母
    {
        for(auto ipos = (*it) + 1 ; ipos != *(++it) + 1 ; ++ipos)
        {
            char const c = str[ipos];
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            {
                std::cout << c << std::endl;
                word_pos = ipos ;
                break;
            }
            /*else
            {
                continue ;
            }*/
        }

        strNum.clear();
        //strNum.erase(); //1 : if you don't want to release the memory but clear the data, theyn you don't need to call erase at here
                          //2 : this is not the correct ways to call erase, please google the proper api or check your c++ primer 5
        for(auto ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字
        {
            char const c = str[ipos];
            if( c >= '0'  && c <= '9')
            {
                strNum += c ;
            }
            else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                continue ;
            }
            /*else
            {
                continue ;
            }*/
        }
    }
}

int main()
{    
    std::cout << "string parse test\n"<< std::endl;

    GetResult("#C255 244 233 22##H5##a1#","#");


    return 0;
}


以下是进阶题材,可进修也可忽略
如果你要设计出很好的API,可以参考boost spirit,这个library利用expression template
的威力,在c++内设计出"domain specific language",提供了非常high level,容易维护,而且
效率极高的api( 大部分情况下比c的API还要更加快

其他的还有matrix template library 4,利用expression template消除temporary parameters
运算效率极高,比许多c程序员写的代码都来得更高

再提醒一次,这些是进阶题材,除非你有志设计出非常高质量的libraries(boost, Qt, MPL4等),否则
你大概一辈子都不用知道什么是expression template

#2


引用 1 楼 stereoMatching 的回复:
我没有检查你的程式正确与否,只是做了一些小小的改动


#include <iostream>
#include <string>
#include <vector>

//const std::string const_str = "#C255 244 233 22##H5##a1#"; //try your best to avoid global parameters, most of the times global params are evils


//检测##的位置以及数目
std::vector<int> GetTagPos(const std::string& str ,const std::string& tag)
{
    //check the tag and str are empty or not first, this is one of the technique which called "lazy evaluation"
    if(tag.empty()  || str.empty()){
        return std::vector<int>();
    }

    std::vector<int> iPosList;
    size_t tag_pos = 0 ;
    size_t search_pos = 0;
    size_t search_length = str.length();

    //iPosList.clear(); //don't need to clear it since it do not have any data at all
    iPosList.reserve(32);
    while(search_pos <= search_length)   //检测tag位置
    {
        if((tag_pos = str.find(tag,search_pos)) != std::string::npos )
        {
            iPosList.push_back(tag_pos);
            search_pos = tag_pos + 1 ;
        }
        else
        {
            break;
        }
    }

    if(iPosList.size() % 2 != 0 )   //如果不配对,返回空
    {
        return std::vector<int>();
    }

    return iPosList; //remove goto, this is c++, not c, with the helps of RAII,we rarely need goto
}


void GetResult(const std::string& str , const std::string& tag )
{
     //check the str is empty or not first, this is one of the technique which called "lazy evaluation"
    if(true == str.empty()){
        return;
    }

    std::vector<int> itagpos = GetTagPos(str, tag); //this way the codes are shorter and compiler has more chances to optimize your codes
    if(itagpos.empty()){
        return ;
    }

    //size_t ipos = 0 ; //don't need to declare your local paorameter at here, you could declare it later on
    size_t word_pos = 0 ;
    std::string strNum;
    auto end = std::end(itagpos); //you could use auto to save you some typing trouble
    for(auto it = std::begin(itagpos); it != end ; ++it)    //首先检测字母
    {
        for(auto ipos = (*it) + 1 ; ipos != *(++it) + 1 ; ++ipos)
        {
            char const c = str[ipos];
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            {
                std::cout << c << std::endl;
                word_pos = ipos ;
                break;
            }
            /*else
            {
                continue ;
            }*/
        }

        strNum.clear();
        //strNum.erase(); //1 : if you don't want to release the memory but clear the data, theyn you don't need to call erase at here
                          //2 : this is not the correct ways to call erase, please google the proper api or check your c++ primer 5
        for(auto ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字
        {
            char const c = str[ipos];
            if( c >= '0'  && c <= '9')
            {
                strNum += c ;
            }
            else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                continue ;
            }
            /*else
            {
                continue ;
            }*/
        }
    }
}

int main()
{    
    std::cout << "string parse test\n"<< std::endl;

    GetResult("#C255 244 233 22##H5##a1#","#");


    return 0;
}


以下是进阶题材,可进修也可忽略
如果你要设计出很好的API,可以参考boost spirit,这个library利用expression template
的威力,在c++内设计出"domain specific language",提供了非常high level,容易维护,而且
效率极高的api( 大部分情况下比c的API还要更加快

其他的还有matrix template library 4,利用expression template消除temporary parameters
运算效率极高,比许多c程序员写的代码都来得更高

再提醒一次,这些是进阶题材,除非你有志设计出非常高质量的libraries(boost, Qt, MPL4等),否则
你大概一辈子都不用知道什么是expression template

哥是来检测英文的
don't need to clear it since it do not have any data at all
need前面加了don't

#3


引用
need前面加了don't 

本来就不需要call clear, iPosList才刚construct,是空的

有两个小地方忘了修改

else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                //continue ; //this could be removed
            }



if(str.empty()){ //str.empty() is enough
        return;
    }

#4


新人表示看不懂。

#5


如果只是为了分开打印:
string s("#C255 244 233 22##H5##a1#");
s.erase(remove(s.begin(),s.end(),'#'),s.end());
bool isnum=false,isnumpre=false;
if(s[0] >= '0'  && s[0] <= '9')
isnum=isnumpre=true;
cout<<s[0];
for(int i=1;i<s.size();i++)
{
if(s[i] >= '0'  && s[i] <= '9')
isnum=true;
else
isnum=false;
if(!(isnumpre && isnum))
{
isnumpre=isnum;
cout<<endl;
}
cout<<s[i];
}
cout<<endl;

#6


不好意思,上面的程序有点问题,修改如下:

string s("#C255 244 233 22z#z#H5##a1#");
char *tc=strtok(const_cast<char*>(s.c_str()),"#");
string temps;
while(tc)
{
        temps=tc;
bool isnum=false,isnumpre=false;
if(temps[0] >= '0'  && temps[0] <= '9')
isnum=isnumpre=true;
cout<<temps[0];
for(int i=1;i<temps.size();i++)
{
if(temps[i] >= '0'  && temps[i] <= '9')
isnum=true;
else
isnum=false;
if(!(isnumpre && isnum))
{
isnumpre=isnum;
cout<<endl;
}
cout<<temps[i];
}
cout<<endl;
tc=strtok(NULL,"#");
}

#7




立于楼主 代码之外说的:
对于字符串的操作, 求优化, 

第一: 至少用 字符串char数组 要比string 高效很多的!
第二:尽量少用多重循环,加循环里加判断,会破坏连续性,
第三:能用容器的就用容器

#8


谢谢各位建议,用意是输出其中的在数字,谢谢#2 #7的建议

楼上提到尽量少用多重循环,有神马办法可以避免循环?STL算法?

#9


不好意思,上面的程序有点问题,修改如下:
C/C++ code

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
string s("#C255 244 233 22z#z#H5##a1#");        
char *tc=strtok(const_cast<char*>(s.c_str()),"#");
string temps;        
while(tc)
{
        temps=tc;
    bool isnum=false,isnumpre=false;
    if(temps[0] >= '0'  && temps[0] <= '9')
        isnum=isnumpre=true;
    cout<<temps[0];
    for(int i=1;i<temps.size();i++)
    {
        if(temps[i] >= '0'  && temps[i] <= '9')
            isnum=true;
        else
            isnum=false;            
        if(!(isnumpre && isnum))
        {
            isnumpre=isnum;
            cout<<endl;
        }
        cout<<temps[i];
    }
    cout<<endl;            
    tc=strtok(NULL,"#");
}

谢谢这位,其实我是想用 ## 来区分不同的数据段,来区分数据,#也可能是 @符号,谢谢你的建议

#10


根本没看懂楼主的问题。

基本都输入输出事例起码给一个吧。

#11


引用 8 楼 opencv2008 的回复:
谢谢各位建议,用意是输出其中的在数字,谢谢#2 #7的建议

楼上提到尽量少用多重循环,有神马办法可以避免循环?STL算法?

不管啥算法,总是要遍历的,用stl也没办法避免。
对于你这个例子,最直接最效率的做法就是取string[i],用循环每个字符判断。这样只需一次遍历。
用了find,再从头去判断,是多次遍历了。(find找的时候也是去遍历的)

#12


楼主还是去google 正则表达式 吧
应该3、4行就可以搞定

#13


我是C++元编程的忠实爱好者。但是,我清楚的认识到,所谓它快的原因是什么。
说比C快,你能够说一下什么情况下比C快吗?或者举几个实例。
别说这么一通吓人的话语,知道的知道是怎么回事儿,不知道的还以为C++又进阶了呐。

引用 1 楼 stereoMatching 的回复:
我没有检查你的程式正确与否,只是做了一些小小的改动


#include <iostream>
#include <string>
#include <vector>

//const std::string const_str = "#C255 244 233 22##H5##a1#"; //try your best to avoid global parameters, most of the times global params are evils

//检测##的位置以及数目
std::vector<int> GetTagPos(const std::string& str ,const std::string& tag)
{
    //check the tag and str are empty or not first, this is one of the technique which called "lazy evaluation"
    if(tag.empty()  || str.empty()){
        return std::vector<int>();
    }

    std::vector<int> iPosList;
    size_t tag_pos = 0 ;
    size_t search_pos = 0;
    size_t search_length = str.length();

    //iPosList.clear(); //don't need to clear it since it do not have any data at all
    iPosList.reserve(32);
    while(search_pos <= search_length)   //检测tag位置
    {
        if((tag_pos = str.find(tag,search_pos)) != std::string::npos )
        {
            iPosList.push_back(tag_pos);
            search_pos = tag_pos + 1 ;
        }
        else
        {
            break;
        }
    }

    if(iPosList.size() % 2 != 0 )   //如果不配对,返回空
    {
        return std::vector<int>();
    }

    return iPosList; //remove goto, this is c++, not c, with the helps of RAII,we rarely need goto
}


void GetResult(const std::string& str , const std::string& tag )
{
     //check the str is empty or not first, this is one of the technique which called "lazy evaluation"
    if(true == str.empty()){
        return;
    }

    std::vector<int> itagpos = GetTagPos(str, tag); //this way the codes are shorter and compiler has more chances to optimize your codes
    if(itagpos.empty()){
        return ;
    }

    //size_t ipos = 0 ; //don't need to declare your local paorameter at here, you could declare it later on
    size_t word_pos = 0 ;
    std::string strNum;
    auto end = std::end(itagpos); //you could use auto to save you some typing trouble
    for(auto it = std::begin(itagpos); it != end ; ++it)    //首先检测字母
    {
        for(auto ipos = (*it) + 1 ; ipos != *(++it) + 1 ; ++ipos)
        {
            char const c = str[ipos];
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            {
                std::cout << c << std::endl;
                word_pos = ipos ;
                break;
            }
            /*else
            {
                continue ;
            }*/
        }

        strNum.clear();
        //strNum.erase(); //1 : if you don't want to release the memory but clear the data, theyn you don't need to call erase at here
                          //2 : this is not the correct ways to call erase, please google the proper api or check your c++ primer 5
        for(auto ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字
        {
            char const c = str[ipos];
            if( c >= '0'  && c <= '9')
            {
                strNum += c ;
            }
            else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                continue ;
            }
            /*else
            {
                continue ;
            }*/
        }
    }
}

int main()
{    
    std::cout << "string parse test\n"<< std::endl;

    GetResult("#C255 244 233 22##H5##a1#","#");


    return 0;
}


以下是进阶题材,可进修也可忽略
如果你要设计出很好的API,可以参考boost spirit,这个library利用expression template
的威力,在c++内设计出"domain specific language",提供了非常high level,容易维护,而且
效率极高的api( 大部分情况下比c的API还要更加快

其他的还有matrix template library 4,利用expression template消除temporary parameters
运算效率极高,比许多c程序员写的代码都来得更高

再提醒一次,这些是进阶题材,除非你有志设计出非常高质量的libraries(boost, Qt, MPL4等),否则
你大概一辈子都不用知道什么是expression template

#14



void process_string(char *tokens, char* str)
{
char bitmap[256] = {0};
int value = 0;

char* ptr = tokens;
while (bitmap[*ptr++]++, *ptr) 
;

ptr = str;
while (*ptr) 
{
if (bitmap[*ptr] > 0)
{
// 输出缓存内容
if (value > 0)
printf("%d\n", value);

value = 0;
} else if ( (*ptr >= 'a' && *ptr <= 'z') // 字母
|| (*ptr >= 'A' && *ptr <= 'Z') )
{
printf("%c\n", *ptr);
} else if (*ptr >= '0' && *ptr <= '9') // 数字
{
value *= 10; // 字符串转化数字
value += (*ptr-'0');
}

ptr++;
}
}
char* dst_str = "#C255 244 233 22##H5##a1#";
process_string("# ", dst_str); // 空格也算分隔符

C
255
244
233
22
H
5
a
1

#15


引用
说比C快,你能够说一下什么情况下比C快吗?或者举几个实例。

以boost spirit为例子

http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/int_performance.html
http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/double_performance.html
http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/format_performance.html


上面是spirit2x的表现,spirit3x的性能可能有机会更上一层楼,而且在语法方面会更简洁
http://boost-spirit.com/home/
c++11确实比c++98好上不少,给予programmers更好的表达能力,也让编译器有更大的空间做优化
2017的concepts,不知道是否能将TMP推上更高的境界?

Matrix Template Library 4的性能量测在这里
http://www.simunova.com/en/node/185

最后要重申一点,其实 c也可以达到一样或更快的运算速度
但是c设计出来的API 绝对不可能 同时 兼顾速度和high level的API

#16


引用 7 楼 max_min_ 的回复:


立于楼主 代码之外说的:
对于字符串的操作, 求优化, 

第一: 至少用 字符串char数组 要比string 高效很多的!
第二:尽量少用多重循环,加循环里加判断,会破坏连续性,
第三:能用容器的就用容器

这个就好

#17


同样一个一个判断

#18


各位大神在开一个板块 讨论C和C++的速度吧,其实软件不光专注与速度,后期的维护同样重要,所以讨论这个不重要。。

#19


写一个字符串分割函数,如下:
/***************** 字符串分割函数
* 函数: int SplitFromSingleChar(const string &desStr, const string &split, vector<string > &vStr);
* 功能: 分割desStr字符串
* 参数说明: desStr:需要分割字符串; split:分割的字符串字符; vStr:分割后的字符串
* 返回值:函数执行成功,返回分割得到的字符串的个数, 如果失败,返回 0 。
* 例如:str = "my name is<hello,word> : test[string](string.)world";
*       split = " ,;:!?.<>[]()'";  //split中的每一个字符都是分隔符
*****/
int SplitBaseOnSingleChar(const string &desStr, const string &split, vector<string > &vStr)
{
string::size_type pos = 0;
string::size_type numBegin = 0;
string str = desStr;
str += split.substr(0, 1);    //确保待分割的字符串末尾有分隔符
while((pos = str.find_first_of(split, pos))!= string::npos )
{
if( pos!= 0 && string::npos == str.substr(pos - 1, 1).find_first_of(split))
{
vStr.push_back(str.substr(numBegin, pos - numBegin ));
}
++pos;
numBegin = pos;
}
return vStr.size();
}

第一次调用:
SplitBaseOnSingleChar(str, "#0123456789", ve); //ve存放了所有的字符
第二次调用之前把ve中的字符串相加成一个str 再次调用函数
SplitBaseOnSingleChar(str, "#", ve); //ve存放了所有数字

#20


为啥子要用 goto ? 还有那些 else { continue; }?
相较比效率,我更倾向于优先“High Level API”这个概念。
所以我 prefer GetTags to GetTagPos
另外, 把任何检测的代码与输出的代码放在一起都有违反SRP的嫌疑

#21


return std::move(iPosList);

#22


应该有正则表达好似吧

#23


引用
为啥子要用 goto ? 还有那些 else { continue; }?
相较比效率,我更倾向于优先“High Level API”这个概念。
所以我 prefer GetTags to GetTagPos
另外, 把任何检测的代码与输出的代码放在一起都有违反SRP的嫌疑


 不知道大家为什么不爱用continue ,个人认为在大型代码中不可避免会用到if else以及循环,用 else {continue;}只是将代码的逻辑更加明了,方便后期的代码维护,多了几行代码,但是对程序的效率应该没有什么影响。。这是个人观点,可能比较菜鸟,求解??????????????

下一个问题,
引用
把任何检测的代码与输出的代码放在一起都有违反SRP的嫌疑

求解SRP的解释,以及代码检测的原则问题,2#提到过“惰性初始化”的问题,我看了一下,确实有这个道理,SRP不知道是不是这种解释

#24


坦白地说,我认为C++很多东西是吃饱了撑的。弄那么多奇技淫巧,不如C的10行代码。

const string const_str = "#C255 244 233 22##H5##a1#";
int main(){
char s1,s2,s3,s4,s5,s6;
char c1,c2,c3;
int n1,n2,n3,n4,n5,n6;
sscanf(const_str.c_str(), "%c%c%d%d%d%d%c%c%c%d%c%c%c%d%c",
&s1,&c1,&n1,&n2,&n3,&n4,&s2,&s3,&c2,&n5,&s4,&s5,&c3,&n6,&s6);
printf("%c\n%d\n%d\n%d\n%d\n%c\n%d\n%c\n%d\n",
c1,n1,n2,n3,n4,c2,n5,c3,n6);
return 0;
}

#25


引用
1
2
3
4
5
6
7
8
9
10
11
const string const_str = "#C255 244 233 22##H5##a1#";
int main(){
    char s1,s2,s3,s4,s5,s6;
    char c1,c2,c3;
    int n1,n2,n3,n4,n5,n6;
    sscanf(const_str.c_str(), "%c%c%d%d%d%d%c%c%c%d%c%c%c%d%c",
        &s1,&c1,&n1,&n2,&n3,&n4,&s2,&s3,&c2,&n5,&s4,&s5,&c3,&n6,&s6);
    printf("%c\n%d\n%d\n%d\n%d\n%c\n%d\n%c\n%d\n",
        c1,n1,n2,n3,n4,c2,n5,c3,n6);
    return 0;
}


哈哈,把这个忘记了。。。。这个帅哥厉害,至少这个问题很简单的解决了

#26


引用 24 楼 gemo 的回复:
[color=#FF0000]坦白地说,我认为C++很多东西是吃饱了撑的。弄那么多奇技淫巧,不如C的10行代码。
[/code]

坦白的说,你这根本是apple比organge,搞不清楚状况
你把问题简化成只能切除固定的排列,问题当然简单
如果string是可变长度的,#的摆放位置不固定呢?

来个比你更“简洁”(apple vs orange 2)的回应
坦白地说,我认为你的代码都是多余的,直接输出答案就好了


#include <iostream>

int main()
{
std::cout<<"C\n255\n244\n233\n22\nH\n5\na\n1\n"<<std::endl;

return 0;
}


#27


引用 21 楼 aihahaheihei 的回复:
return std::move(iPosList);

这情况下不必这么做,因为iPosList是local的variable,编译器会尝试用最有效率的方法把他return
你直接表明要move反而会影响优化(RVO or NRVO)

需要明确加上move的是这种状况

std::vector<int> moveBack(std::vector<int> &a)
{
  //.....
  return std::move(a);
}


一些簡單的試驗

std::vector<int> moveBack(std::vector<int> &a)
{
    //need to add move back explicitly, since the compiler can not sure the a is needless anymore
    return std::move(a);
}

std::vector<int> moveBackError(std::vector<int> &a)
{
    return a;
}

int main(){

    {
        std::vector<int> a{1, 2, 3, 4, 5};
        auto b = moveBack(a);
        std::cout<<a.size()<<std::endl;
        std::cout<<b.size()<<std::endl;
    }

    {
        std::vector<int> a{1, 2, 3, 4, 5};
        auto b = moveBackError(a);
        std::cout<<a.size()<<std::endl;
        std::cout<<b.size()<<std::endl;
    }    

    return 0;
}


完整的解答
引用
http://*.com/questions/13430831/return-by-rvalue-reference


来csdn主要是放松+灌水的

#28


引用
不知道大家为什么不爱用continue ,个人认为在大型代码中不可避免会用到if else以及循环,

不是不用,只是你的例子没有必要没必要

引用
用 else {continue;}只是将代码的逻辑更加明了,方便后期的代码维护

一般上不会有人这么写,因为大部分人都认为那行是多余地
这就跟注解一样,写注解和文件虽然是一个好习惯,但是请不要把programmers当成白痴

我觉得加上注解可以帮助日后地维护,所以我加上一堆注解

//i is an integer, an index
//the value of i will change from 0~9 in this loop
for(int i = 0; i != 10; ++i)
{
  //output the value of i
  std::cout<<i<<std::endl;
}

#29


引用 26 楼 stereoMatching 的回复:
Quote: 引用 24 楼 gemo 的回复:

[color=#FF0000]坦白地说,我认为C++很多东西是吃饱了撑的。弄那么多奇技淫巧,不如C的10行代码。
[/code]

坦白的说,你这根本是apple比organge,搞不清楚状况
你把问题简化成只能切除固定的排列,问题当然简单
如果string是可变长度的,#的摆放位置不固定呢?

来个比你更“简洁”(apple vs orange 2)的回应
坦白地说,我认为你的代码都是多余的,直接输出答案就好了


#include <iostream>

int main()
{
std::cout<<"C\n255\n244\n233\n22\nH\n5\na\n1\n"<<std::endl;

return 0;
}




你这人真是没救了,非要死扛。如果#号的位置不固定,LZ自己写的方法直接运行时报错。要不要自己跑一遍?

#30


还有哦,如果#号位置不固定,你在1楼写的程序也是直接挂了哦!
这就是你弄了一大堆C++代码带来的 安全性可扩展性

我的C代码至少挪了位置没挂吧?

#31


引用 30 楼 gemo 的回复:
还有哦,如果#号位置不固定,你在1楼写的程序也是直接挂了哦!
这就是你弄了一大堆C++代码带来的 安全性可扩展性

我的C代码至少挪了位置没挂吧?

我2楼说过了,我没有检查楼主的逻辑,只是对一些编码习惯做修改而已
楼主会那样写,明显是想得到一个较具弹性地函数
你却拿apple比orange,提出一个限制多多的解法后再来说c++的特性没用?

所以我也学你来个apple比orange
直接cout就够了,你说我没救,其实是在说你自己啊
我们的行为都是apple比orange

#32


BOOL splitString(char *_Src, char **_Words, char **_Nums)
{
    if (_Src == NULL) return FALSE;
    if (_Words == NULL) return FALSE;
    if (_Nums == NULL) return FALSE;

    size_t len = ::strlen(_Src);
    char *word = new char[len + 1];
    char *num = new char[len + 1];

    ::memset(word, 0, len + 1);
    ::memset(num, 0, len + 1);

    *_Words = word;
    *_Nums = num;

    char *p = _Src;
    do
    {
        if (*p >= '0' && *p <= '9')
            *num++ = *p;
        else if (*p >= 'a' && *p <= 'z')
            *word++ = *p;
        else if (*p >= 'A' && *p <= 'Z')
            *word++ = *p;
    } while (*p++);

    return TRUE;
}


调用
char chSource[] = "#C255 244 233 12#   #d4#       #f5";
char *words, *nums;
splitString(chSource, &words, &nums);
::printf("%s\n", words);
::printf("%s\n", nums);
delete[] words;
delete[] nums;

#33


引用 31 楼 stereoMatching 的回复:
Quote: 引用 30 楼 gemo 的回复:

还有哦,如果#号位置不固定,你在1楼写的程序也是直接挂了哦!
这就是你弄了一大堆C++代码带来的 安全性可扩展性

我的C代码至少挪了位置没挂吧?

我2楼说过了,我没有检查楼主的逻辑,只是对一些编码习惯做修改而已
楼主会那样写,明显是想得到一个较具弹性地函数
你却拿apple比orange,提出一个限制多多的解法后再来说c++的特性没用?

所以我也学你来个apple比orange
直接cout就够了,你说我没救,其实是在说你自己啊
我们的行为都是apple比orange


我只知道如果#号位置不固定,你的代码挂了

#34


输出

Cdf
2552442331245

#35


代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

#36


如果楼主希望让程式更容易维护
可以考虑使用std::regex,这是c++11正式支援的regular expression
至于我之前提到的boost::spirit, 虽然大部分情况下性能比c提供的标准函数还要快
但是他需要你对c++有比较深入的了解和掌握,事后有兴趣再接触也可以

给楼主另外一个版本的实现,已经把template去掉,原版的设计是希望可以支援不同的"string"和"result"
不过楼主大概刚接触c++不久,所以我先介绍你比较简单的

void split(std::string const& contents, std::vector<std::string> &result, std::string const& delimiters = "\n")
{

    std::string::size_type pos, last_pos = 0;
    while(true)
    {
        pos = contents.find_first_of(delimiters, last_pos);
        if(pos == std::string::npos)
        {
            pos = contents.length();

            if(pos != last_pos)
                result.emplace_back(contents.data() + last_pos, pos - last_pos);

            break;
        }
        else
        {
            if(pos != last_pos)
                result.emplace_back(contents.data() + last_pos, pos - last_pos );
        }

        last_pos = pos + 1;
    }
}

void test_stl()
{
    std::cout<<"get number"<<std::endl;
    std::vector<std::string> strs;
    split("#C255 244 233 22##H5##a1#", strs, "#CHa ");
    for(auto const &data : strs){
        std::cout<<data<<std::endl;
    }
}


output
255
244
233
22
5
1

希望这是楼主想要的输出

我把result设计成parameters而非return by function
是为了让memory(std::vector<std::string>)可以重复利用
如果你需要更简洁的api,大可以改成return by function

#37


引用 35 楼 gemo 的回复:
代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

你的代码改点输入不是一样会挂。#号多写几个的情况下。

#38


简单的示范一下怎么截取char


std::cout<<"get number"<<std::endl;
    std::vector<std::string> strs;
    split("#C25#5# 244 233 22##H5##a1#", strs, "#CHa ");
    for(auto const &data : strs){
        std::cout<<data<<std::endl;
    }

    std::cout<<"get char"<<std::endl;
    strs.clear(); //this way the memory already allocated could be resued
    split("#C25#5# 244 233 22##H5##a1#", strs, "12345# ");
    for(auto const &data : strs){
        std::cout<<data<<std::endl;
    }

#39


Cpp库里没有这个函数(包括正则表达式)吗?

#40


我也很希望他有切割字串的函数,大部分情况下当然还是使用世界最顶尖的人开发出来的
东西会比较有保障,轻松,好维护
现在因为都在用QString,这方面的问题被简化了很多(QString有支援split)

至于正则表达式,c++11已经有支援了

#41


引用 37 楼 FancyMouse 的回复:
Quote: 引用 35 楼 gemo 的回复:

代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

你的代码改点输入不是一样会挂。#号多写几个的情况下。


能不能告诉下要多些几个#号的情况下会挂?我试了没问题呢?
C++字符串解析分享,求优化

#42


引用 37 楼 FancyMouse 的回复:
Quote: 引用 35 楼 gemo 的回复:

代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

你的代码改点输入不是一样会挂。#号多写几个的情况下。

不要跟猪玩

#43


大家来看看LS的素质

#44


这明明可以用正规文法描述的玩意, 怎么有人还在扯用基于上下文无关文法的spirit的有可能更快,无关的规约那么多,不慢个两三倍就看得起它了, 有这样扯淡的么...

听大妈的用正则表达式就可以了, 当然转成DFA的方式会更好.

#45


谢谢stereoMatching的讲解和修改,我的需求确实是这样的,#这个符号 是不确定的,可能会是  @ ¥%等等吧,除了##之间肯定会有字母 数字之外,数字的多少 ,位置 其实没固定。

其实我接触C++一段时间了,对语言中的细节接触还是太少,谢谢各位啦哈, 准备结贴,下个问题继续讨论string

#46


引用 44 楼 mLee79 的回复:
怎么有人还在扯用基于上下文无关文法的spirit的有可能更快,无关的规约那么多,不慢个两三倍就看得起它了, 有这样扯淡的么...

spirit2的主要优点有两个,一个是快(公认的,benchmark就摆在眼前,而且spirit3会比现在更快);一个是类似EBNF的语法可以让程式容易维护。
如果他不够快,我相信不会有什么人想使用这东西,他的学习门槛和编译时间不值得我们去承受

缺点 : 
由于非常依赖编译器的运算,编译速度极慢;
编译错误难以解读,建议从后面读起(backtrace);
各种type的宣告非常麻烦


std::string target("#C25#5# 244 233 22##H5##a1#");
    target.reserve(target.size() * 15);
    for(size_t i = 0; i != 15; ++i){
        target += target;
        std::cout<<i<<std::endl;
    }
    {
        timeEstimate<> time;
        //std::cout<<"get number"<<std::endl;
        std::vector<std::string> strs;
        for(size_t i = 0; i != 100; ++i){
            split(target, strs, "#CHa "); //我在第36楼的实现
            strs.clear();
        }
        //for(auto const &data : strs){
        //    std::cout<<data<<std::endl;
        //}
    }

    {
        using boost::spirit::qi::double_;
        using boost::spirit::qi::char_;
        using boost::spirit::qi::_1;
        using boost::spirit::qi::phrase_parse;
        using boost::spirit::ascii::space;
        using boost::phoenix::push_back;
        using boost::phoenix::ref;

        {
            timeEstimate<> time;
            std::vector<int> v;
            for(size_t i = 0; i != 100; ++i){
                phrase_parse(target.cbegin(), target.cend(),
                             (
                                 *( *char_("#CHa")>> double_[push_back(boost::phoenix::ref(v), _1)])
                             //double_[push_back(boost::phoenix::ref(v), _1)]) % "#"
                             )
                        ,
                        space);
                v.clear();
            }

        }

    }


output
Elapsed time : 1.059
Elapsed time : 0.551

引用 44 楼 mLee79 的回复:
听大妈的用正则表达式就可以了, 当然转成DFA的方式会更好.

确实用正则就好了,

#47


编译原理是门很成熟的玩意了, 没有spirit的几十年前大家就直接写EBNF自动生成代码了, 那东西没啥意思, 速度也不会比第三方的LALR, LL parser跑的更快 ..

能用有穷自动机描述的东西就不要用下推自动机去做, 性能就不是一个档次的, 同样的文法, PDA比 DFA只慢了2,3倍就算人品好了, 并且有穷自动机代码简单, 容易理解 ..

#48


引用 47 楼 mLee79 的回复:
编译原理是门很成熟的玩意了, 没有spirit的几十年前大家就直接写EBNF自动生成代码了, 那东西没啥意思, 速度也不会比第三方的LALR, LL parser跑的更快 ..

能用有穷自动机描述的东西就不要用下推自动机去做, 性能就不是一个档次的, 同样的文法, PDA比 DFA只慢了2,3倍就算人品好了, 并且有穷自动机代码简单, 容易理解 ..

我忘了另外一个好处,就是你可以直接在c++中使用
好歹他的速度还是比regular expression快
并且也比大部分水准普通(例如我)的人写的代码快
甚至比c的函数还要更快

你说的很有道理,但不是所有人都想用LALR, LL parser之类的工具写parser的
spirit至少提供了另外一个选择,有兴趣的人自然会去学习他
其实spirit这东西我是很久没碰了,我不需要什么性能很高的parser
一般的regular expression就能满足我的需求
不过spirit3出现时我应该还是会下来玩一玩的
spirit3的语法比起spirit2是简洁了不少,性能也提高了不少

#50


ls来秀一把你的代码

#1


我没有检查你的程式正确与否,只是做了一些小小的改动


#include <iostream>
#include <string>
#include <vector>

//const std::string const_str = "#C255 244 233 22##H5##a1#"; //try your best to avoid global parameters, most of the times global params are evils

//检测##的位置以及数目
std::vector<int> GetTagPos(const std::string& str ,const std::string& tag)
{
    //check the tag and str are empty or not first, this is one of the technique which called "lazy evaluation"
    if(tag.empty()  || str.empty()){
        return std::vector<int>();
    }

    std::vector<int> iPosList;
    size_t tag_pos = 0 ;
    size_t search_pos = 0;
    size_t search_length = str.length();

    //iPosList.clear(); //don't need to clear it since it do not have any data at all
    iPosList.reserve(32);
    while(search_pos <= search_length)   //检测tag位置
    {
        if((tag_pos = str.find(tag,search_pos)) != std::string::npos )
        {
            iPosList.push_back(tag_pos);
            search_pos = tag_pos + 1 ;
        }
        else
        {
            break;
        }
    }

    if(iPosList.size() % 2 != 0 )   //如果不配对,返回空
    {
        return std::vector<int>();
    }

    return iPosList; //remove goto, this is c++, not c, with the helps of RAII,we rarely need goto
}


void GetResult(const std::string& str , const std::string& tag )
{
     //check the str is empty or not first, this is one of the technique which called "lazy evaluation"
    if(true == str.empty()){
        return;
    }

    std::vector<int> itagpos = GetTagPos(str, tag); //this way the codes are shorter and compiler has more chances to optimize your codes
    if(itagpos.empty()){
        return ;
    }

    //size_t ipos = 0 ; //don't need to declare your local paorameter at here, you could declare it later on
    size_t word_pos = 0 ;
    std::string strNum;
    auto end = std::end(itagpos); //you could use auto to save you some typing trouble
    for(auto it = std::begin(itagpos); it != end ; ++it)    //首先检测字母
    {
        for(auto ipos = (*it) + 1 ; ipos != *(++it) + 1 ; ++ipos)
        {
            char const c = str[ipos];
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            {
                std::cout << c << std::endl;
                word_pos = ipos ;
                break;
            }
            /*else
            {
                continue ;
            }*/
        }

        strNum.clear();
        //strNum.erase(); //1 : if you don't want to release the memory but clear the data, theyn you don't need to call erase at here
                          //2 : this is not the correct ways to call erase, please google the proper api or check your c++ primer 5
        for(auto ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字
        {
            char const c = str[ipos];
            if( c >= '0'  && c <= '9')
            {
                strNum += c ;
            }
            else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                continue ;
            }
            /*else
            {
                continue ;
            }*/
        }
    }
}

int main()
{    
    std::cout << "string parse test\n"<< std::endl;

    GetResult("#C255 244 233 22##H5##a1#","#");


    return 0;
}


以下是进阶题材,可进修也可忽略
如果你要设计出很好的API,可以参考boost spirit,这个library利用expression template
的威力,在c++内设计出"domain specific language",提供了非常high level,容易维护,而且
效率极高的api( 大部分情况下比c的API还要更加快

其他的还有matrix template library 4,利用expression template消除temporary parameters
运算效率极高,比许多c程序员写的代码都来得更高

再提醒一次,这些是进阶题材,除非你有志设计出非常高质量的libraries(boost, Qt, MPL4等),否则
你大概一辈子都不用知道什么是expression template

#2


引用 1 楼 stereoMatching 的回复:
我没有检查你的程式正确与否,只是做了一些小小的改动


#include <iostream>
#include <string>
#include <vector>

//const std::string const_str = "#C255 244 233 22##H5##a1#"; //try your best to avoid global parameters, most of the times global params are evils


//检测##的位置以及数目
std::vector<int> GetTagPos(const std::string& str ,const std::string& tag)
{
    //check the tag and str are empty or not first, this is one of the technique which called "lazy evaluation"
    if(tag.empty()  || str.empty()){
        return std::vector<int>();
    }

    std::vector<int> iPosList;
    size_t tag_pos = 0 ;
    size_t search_pos = 0;
    size_t search_length = str.length();

    //iPosList.clear(); //don't need to clear it since it do not have any data at all
    iPosList.reserve(32);
    while(search_pos <= search_length)   //检测tag位置
    {
        if((tag_pos = str.find(tag,search_pos)) != std::string::npos )
        {
            iPosList.push_back(tag_pos);
            search_pos = tag_pos + 1 ;
        }
        else
        {
            break;
        }
    }

    if(iPosList.size() % 2 != 0 )   //如果不配对,返回空
    {
        return std::vector<int>();
    }

    return iPosList; //remove goto, this is c++, not c, with the helps of RAII,we rarely need goto
}


void GetResult(const std::string& str , const std::string& tag )
{
     //check the str is empty or not first, this is one of the technique which called "lazy evaluation"
    if(true == str.empty()){
        return;
    }

    std::vector<int> itagpos = GetTagPos(str, tag); //this way the codes are shorter and compiler has more chances to optimize your codes
    if(itagpos.empty()){
        return ;
    }

    //size_t ipos = 0 ; //don't need to declare your local paorameter at here, you could declare it later on
    size_t word_pos = 0 ;
    std::string strNum;
    auto end = std::end(itagpos); //you could use auto to save you some typing trouble
    for(auto it = std::begin(itagpos); it != end ; ++it)    //首先检测字母
    {
        for(auto ipos = (*it) + 1 ; ipos != *(++it) + 1 ; ++ipos)
        {
            char const c = str[ipos];
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            {
                std::cout << c << std::endl;
                word_pos = ipos ;
                break;
            }
            /*else
            {
                continue ;
            }*/
        }

        strNum.clear();
        //strNum.erase(); //1 : if you don't want to release the memory but clear the data, theyn you don't need to call erase at here
                          //2 : this is not the correct ways to call erase, please google the proper api or check your c++ primer 5
        for(auto ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字
        {
            char const c = str[ipos];
            if( c >= '0'  && c <= '9')
            {
                strNum += c ;
            }
            else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                continue ;
            }
            /*else
            {
                continue ;
            }*/
        }
    }
}

int main()
{    
    std::cout << "string parse test\n"<< std::endl;

    GetResult("#C255 244 233 22##H5##a1#","#");


    return 0;
}


以下是进阶题材,可进修也可忽略
如果你要设计出很好的API,可以参考boost spirit,这个library利用expression template
的威力,在c++内设计出"domain specific language",提供了非常high level,容易维护,而且
效率极高的api( 大部分情况下比c的API还要更加快

其他的还有matrix template library 4,利用expression template消除temporary parameters
运算效率极高,比许多c程序员写的代码都来得更高

再提醒一次,这些是进阶题材,除非你有志设计出非常高质量的libraries(boost, Qt, MPL4等),否则
你大概一辈子都不用知道什么是expression template

哥是来检测英文的
don't need to clear it since it do not have any data at all
need前面加了don't

#3


引用
need前面加了don't 

本来就不需要call clear, iPosList才刚construct,是空的

有两个小地方忘了修改

else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                //continue ; //this could be removed
            }



if(str.empty()){ //str.empty() is enough
        return;
    }

#4


新人表示看不懂。

#5


如果只是为了分开打印:
string s("#C255 244 233 22##H5##a1#");
s.erase(remove(s.begin(),s.end(),'#'),s.end());
bool isnum=false,isnumpre=false;
if(s[0] >= '0'  && s[0] <= '9')
isnum=isnumpre=true;
cout<<s[0];
for(int i=1;i<s.size();i++)
{
if(s[i] >= '0'  && s[i] <= '9')
isnum=true;
else
isnum=false;
if(!(isnumpre && isnum))
{
isnumpre=isnum;
cout<<endl;
}
cout<<s[i];
}
cout<<endl;

#6


不好意思,上面的程序有点问题,修改如下:

string s("#C255 244 233 22z#z#H5##a1#");
char *tc=strtok(const_cast<char*>(s.c_str()),"#");
string temps;
while(tc)
{
        temps=tc;
bool isnum=false,isnumpre=false;
if(temps[0] >= '0'  && temps[0] <= '9')
isnum=isnumpre=true;
cout<<temps[0];
for(int i=1;i<temps.size();i++)
{
if(temps[i] >= '0'  && temps[i] <= '9')
isnum=true;
else
isnum=false;
if(!(isnumpre && isnum))
{
isnumpre=isnum;
cout<<endl;
}
cout<<temps[i];
}
cout<<endl;
tc=strtok(NULL,"#");
}

#7




立于楼主 代码之外说的:
对于字符串的操作, 求优化, 

第一: 至少用 字符串char数组 要比string 高效很多的!
第二:尽量少用多重循环,加循环里加判断,会破坏连续性,
第三:能用容器的就用容器

#8


谢谢各位建议,用意是输出其中的在数字,谢谢#2 #7的建议

楼上提到尽量少用多重循环,有神马办法可以避免循环?STL算法?

#9


不好意思,上面的程序有点问题,修改如下:
C/C++ code

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
string s("#C255 244 233 22z#z#H5##a1#");        
char *tc=strtok(const_cast<char*>(s.c_str()),"#");
string temps;        
while(tc)
{
        temps=tc;
    bool isnum=false,isnumpre=false;
    if(temps[0] >= '0'  && temps[0] <= '9')
        isnum=isnumpre=true;
    cout<<temps[0];
    for(int i=1;i<temps.size();i++)
    {
        if(temps[i] >= '0'  && temps[i] <= '9')
            isnum=true;
        else
            isnum=false;            
        if(!(isnumpre && isnum))
        {
            isnumpre=isnum;
            cout<<endl;
        }
        cout<<temps[i];
    }
    cout<<endl;            
    tc=strtok(NULL,"#");
}

谢谢这位,其实我是想用 ## 来区分不同的数据段,来区分数据,#也可能是 @符号,谢谢你的建议

#10


根本没看懂楼主的问题。

基本都输入输出事例起码给一个吧。

#11


引用 8 楼 opencv2008 的回复:
谢谢各位建议,用意是输出其中的在数字,谢谢#2 #7的建议

楼上提到尽量少用多重循环,有神马办法可以避免循环?STL算法?

不管啥算法,总是要遍历的,用stl也没办法避免。
对于你这个例子,最直接最效率的做法就是取string[i],用循环每个字符判断。这样只需一次遍历。
用了find,再从头去判断,是多次遍历了。(find找的时候也是去遍历的)

#12


楼主还是去google 正则表达式 吧
应该3、4行就可以搞定

#13


我是C++元编程的忠实爱好者。但是,我清楚的认识到,所谓它快的原因是什么。
说比C快,你能够说一下什么情况下比C快吗?或者举几个实例。
别说这么一通吓人的话语,知道的知道是怎么回事儿,不知道的还以为C++又进阶了呐。

引用 1 楼 stereoMatching 的回复:
我没有检查你的程式正确与否,只是做了一些小小的改动


#include <iostream>
#include <string>
#include <vector>

//const std::string const_str = "#C255 244 233 22##H5##a1#"; //try your best to avoid global parameters, most of the times global params are evils

//检测##的位置以及数目
std::vector<int> GetTagPos(const std::string& str ,const std::string& tag)
{
    //check the tag and str are empty or not first, this is one of the technique which called "lazy evaluation"
    if(tag.empty()  || str.empty()){
        return std::vector<int>();
    }

    std::vector<int> iPosList;
    size_t tag_pos = 0 ;
    size_t search_pos = 0;
    size_t search_length = str.length();

    //iPosList.clear(); //don't need to clear it since it do not have any data at all
    iPosList.reserve(32);
    while(search_pos <= search_length)   //检测tag位置
    {
        if((tag_pos = str.find(tag,search_pos)) != std::string::npos )
        {
            iPosList.push_back(tag_pos);
            search_pos = tag_pos + 1 ;
        }
        else
        {
            break;
        }
    }

    if(iPosList.size() % 2 != 0 )   //如果不配对,返回空
    {
        return std::vector<int>();
    }

    return iPosList; //remove goto, this is c++, not c, with the helps of RAII,we rarely need goto
}


void GetResult(const std::string& str , const std::string& tag )
{
     //check the str is empty or not first, this is one of the technique which called "lazy evaluation"
    if(true == str.empty()){
        return;
    }

    std::vector<int> itagpos = GetTagPos(str, tag); //this way the codes are shorter and compiler has more chances to optimize your codes
    if(itagpos.empty()){
        return ;
    }

    //size_t ipos = 0 ; //don't need to declare your local paorameter at here, you could declare it later on
    size_t word_pos = 0 ;
    std::string strNum;
    auto end = std::end(itagpos); //you could use auto to save you some typing trouble
    for(auto it = std::begin(itagpos); it != end ; ++it)    //首先检测字母
    {
        for(auto ipos = (*it) + 1 ; ipos != *(++it) + 1 ; ++ipos)
        {
            char const c = str[ipos];
            if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
            {
                std::cout << c << std::endl;
                word_pos = ipos ;
                break;
            }
            /*else
            {
                continue ;
            }*/
        }

        strNum.clear();
        //strNum.erase(); //1 : if you don't want to release the memory but clear the data, theyn you don't need to call erase at here
                          //2 : this is not the correct ways to call erase, please google the proper api or check your c++ primer 5
        for(auto ipos = word_pos ; ipos != *it+1 ; ++ipos)//检测数字
        {
            char const c = str[ipos];
            if( c >= '0'  && c <= '9')
            {
                strNum += c ;
            }
            else if( (c == ' ' ||  ipos == *it ) && !strNum.empty())
            {
                std::cout << strNum << "\n"<< std::endl;   // 输出了字符,可以转换为数字
                strNum.clear();
                continue ;
            }
            /*else
            {
                continue ;
            }*/
        }
    }
}

int main()
{    
    std::cout << "string parse test\n"<< std::endl;

    GetResult("#C255 244 233 22##H5##a1#","#");


    return 0;
}


以下是进阶题材,可进修也可忽略
如果你要设计出很好的API,可以参考boost spirit,这个library利用expression template
的威力,在c++内设计出"domain specific language",提供了非常high level,容易维护,而且
效率极高的api( 大部分情况下比c的API还要更加快

其他的还有matrix template library 4,利用expression template消除temporary parameters
运算效率极高,比许多c程序员写的代码都来得更高

再提醒一次,这些是进阶题材,除非你有志设计出非常高质量的libraries(boost, Qt, MPL4等),否则
你大概一辈子都不用知道什么是expression template

#14



void process_string(char *tokens, char* str)
{
char bitmap[256] = {0};
int value = 0;

char* ptr = tokens;
while (bitmap[*ptr++]++, *ptr) 
;

ptr = str;
while (*ptr) 
{
if (bitmap[*ptr] > 0)
{
// 输出缓存内容
if (value > 0)
printf("%d\n", value);

value = 0;
} else if ( (*ptr >= 'a' && *ptr <= 'z') // 字母
|| (*ptr >= 'A' && *ptr <= 'Z') )
{
printf("%c\n", *ptr);
} else if (*ptr >= '0' && *ptr <= '9') // 数字
{
value *= 10; // 字符串转化数字
value += (*ptr-'0');
}

ptr++;
}
}
char* dst_str = "#C255 244 233 22##H5##a1#";
process_string("# ", dst_str); // 空格也算分隔符

C
255
244
233
22
H
5
a
1

#15


引用
说比C快,你能够说一下什么情况下比C快吗?或者举几个实例。

以boost spirit为例子

http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/int_performance.html
http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/double_performance.html
http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/format_performance.html


上面是spirit2x的表现,spirit3x的性能可能有机会更上一层楼,而且在语法方面会更简洁
http://boost-spirit.com/home/
c++11确实比c++98好上不少,给予programmers更好的表达能力,也让编译器有更大的空间做优化
2017的concepts,不知道是否能将TMP推上更高的境界?

Matrix Template Library 4的性能量测在这里
http://www.simunova.com/en/node/185

最后要重申一点,其实 c也可以达到一样或更快的运算速度
但是c设计出来的API 绝对不可能 同时 兼顾速度和high level的API

#16


引用 7 楼 max_min_ 的回复:


立于楼主 代码之外说的:
对于字符串的操作, 求优化, 

第一: 至少用 字符串char数组 要比string 高效很多的!
第二:尽量少用多重循环,加循环里加判断,会破坏连续性,
第三:能用容器的就用容器

这个就好

#17


同样一个一个判断

#18


各位大神在开一个板块 讨论C和C++的速度吧,其实软件不光专注与速度,后期的维护同样重要,所以讨论这个不重要。。

#19


写一个字符串分割函数,如下:
/***************** 字符串分割函数
* 函数: int SplitFromSingleChar(const string &desStr, const string &split, vector<string > &vStr);
* 功能: 分割desStr字符串
* 参数说明: desStr:需要分割字符串; split:分割的字符串字符; vStr:分割后的字符串
* 返回值:函数执行成功,返回分割得到的字符串的个数, 如果失败,返回 0 。
* 例如:str = "my name is<hello,word> : test[string](string.)world";
*       split = " ,;:!?.<>[]()'";  //split中的每一个字符都是分隔符
*****/
int SplitBaseOnSingleChar(const string &desStr, const string &split, vector<string > &vStr)
{
string::size_type pos = 0;
string::size_type numBegin = 0;
string str = desStr;
str += split.substr(0, 1);    //确保待分割的字符串末尾有分隔符
while((pos = str.find_first_of(split, pos))!= string::npos )
{
if( pos!= 0 && string::npos == str.substr(pos - 1, 1).find_first_of(split))
{
vStr.push_back(str.substr(numBegin, pos - numBegin ));
}
++pos;
numBegin = pos;
}
return vStr.size();
}

第一次调用:
SplitBaseOnSingleChar(str, "#0123456789", ve); //ve存放了所有的字符
第二次调用之前把ve中的字符串相加成一个str 再次调用函数
SplitBaseOnSingleChar(str, "#", ve); //ve存放了所有数字

#20


为啥子要用 goto ? 还有那些 else { continue; }?
相较比效率,我更倾向于优先“High Level API”这个概念。
所以我 prefer GetTags to GetTagPos
另外, 把任何检测的代码与输出的代码放在一起都有违反SRP的嫌疑

#21


return std::move(iPosList);

#22


应该有正则表达好似吧

#23


引用
为啥子要用 goto ? 还有那些 else { continue; }?
相较比效率,我更倾向于优先“High Level API”这个概念。
所以我 prefer GetTags to GetTagPos
另外, 把任何检测的代码与输出的代码放在一起都有违反SRP的嫌疑


 不知道大家为什么不爱用continue ,个人认为在大型代码中不可避免会用到if else以及循环,用 else {continue;}只是将代码的逻辑更加明了,方便后期的代码维护,多了几行代码,但是对程序的效率应该没有什么影响。。这是个人观点,可能比较菜鸟,求解??????????????

下一个问题,
引用
把任何检测的代码与输出的代码放在一起都有违反SRP的嫌疑

求解SRP的解释,以及代码检测的原则问题,2#提到过“惰性初始化”的问题,我看了一下,确实有这个道理,SRP不知道是不是这种解释

#24


坦白地说,我认为C++很多东西是吃饱了撑的。弄那么多奇技淫巧,不如C的10行代码。

const string const_str = "#C255 244 233 22##H5##a1#";
int main(){
char s1,s2,s3,s4,s5,s6;
char c1,c2,c3;
int n1,n2,n3,n4,n5,n6;
sscanf(const_str.c_str(), "%c%c%d%d%d%d%c%c%c%d%c%c%c%d%c",
&s1,&c1,&n1,&n2,&n3,&n4,&s2,&s3,&c2,&n5,&s4,&s5,&c3,&n6,&s6);
printf("%c\n%d\n%d\n%d\n%d\n%c\n%d\n%c\n%d\n",
c1,n1,n2,n3,n4,c2,n5,c3,n6);
return 0;
}

#25


引用
1
2
3
4
5
6
7
8
9
10
11
const string const_str = "#C255 244 233 22##H5##a1#";
int main(){
    char s1,s2,s3,s4,s5,s6;
    char c1,c2,c3;
    int n1,n2,n3,n4,n5,n6;
    sscanf(const_str.c_str(), "%c%c%d%d%d%d%c%c%c%d%c%c%c%d%c",
        &s1,&c1,&n1,&n2,&n3,&n4,&s2,&s3,&c2,&n5,&s4,&s5,&c3,&n6,&s6);
    printf("%c\n%d\n%d\n%d\n%d\n%c\n%d\n%c\n%d\n",
        c1,n1,n2,n3,n4,c2,n5,c3,n6);
    return 0;
}


哈哈,把这个忘记了。。。。这个帅哥厉害,至少这个问题很简单的解决了

#26


引用 24 楼 gemo 的回复:
[color=#FF0000]坦白地说,我认为C++很多东西是吃饱了撑的。弄那么多奇技淫巧,不如C的10行代码。
[/code]

坦白的说,你这根本是apple比organge,搞不清楚状况
你把问题简化成只能切除固定的排列,问题当然简单
如果string是可变长度的,#的摆放位置不固定呢?

来个比你更“简洁”(apple vs orange 2)的回应
坦白地说,我认为你的代码都是多余的,直接输出答案就好了


#include <iostream>

int main()
{
std::cout<<"C\n255\n244\n233\n22\nH\n5\na\n1\n"<<std::endl;

return 0;
}


#27


引用 21 楼 aihahaheihei 的回复:
return std::move(iPosList);

这情况下不必这么做,因为iPosList是local的variable,编译器会尝试用最有效率的方法把他return
你直接表明要move反而会影响优化(RVO or NRVO)

需要明确加上move的是这种状况

std::vector<int> moveBack(std::vector<int> &a)
{
  //.....
  return std::move(a);
}


一些簡單的試驗

std::vector<int> moveBack(std::vector<int> &a)
{
    //need to add move back explicitly, since the compiler can not sure the a is needless anymore
    return std::move(a);
}

std::vector<int> moveBackError(std::vector<int> &a)
{
    return a;
}

int main(){

    {
        std::vector<int> a{1, 2, 3, 4, 5};
        auto b = moveBack(a);
        std::cout<<a.size()<<std::endl;
        std::cout<<b.size()<<std::endl;
    }

    {
        std::vector<int> a{1, 2, 3, 4, 5};
        auto b = moveBackError(a);
        std::cout<<a.size()<<std::endl;
        std::cout<<b.size()<<std::endl;
    }    

    return 0;
}


完整的解答
引用
http://*.com/questions/13430831/return-by-rvalue-reference


来csdn主要是放松+灌水的

#28


引用
不知道大家为什么不爱用continue ,个人认为在大型代码中不可避免会用到if else以及循环,

不是不用,只是你的例子没有必要没必要

引用
用 else {continue;}只是将代码的逻辑更加明了,方便后期的代码维护

一般上不会有人这么写,因为大部分人都认为那行是多余地
这就跟注解一样,写注解和文件虽然是一个好习惯,但是请不要把programmers当成白痴

我觉得加上注解可以帮助日后地维护,所以我加上一堆注解

//i is an integer, an index
//the value of i will change from 0~9 in this loop
for(int i = 0; i != 10; ++i)
{
  //output the value of i
  std::cout<<i<<std::endl;
}

#29


引用 26 楼 stereoMatching 的回复:
Quote: 引用 24 楼 gemo 的回复:

[color=#FF0000]坦白地说,我认为C++很多东西是吃饱了撑的。弄那么多奇技淫巧,不如C的10行代码。
[/code]

坦白的说,你这根本是apple比organge,搞不清楚状况
你把问题简化成只能切除固定的排列,问题当然简单
如果string是可变长度的,#的摆放位置不固定呢?

来个比你更“简洁”(apple vs orange 2)的回应
坦白地说,我认为你的代码都是多余的,直接输出答案就好了


#include <iostream>

int main()
{
std::cout<<"C\n255\n244\n233\n22\nH\n5\na\n1\n"<<std::endl;

return 0;
}




你这人真是没救了,非要死扛。如果#号的位置不固定,LZ自己写的方法直接运行时报错。要不要自己跑一遍?

#30


还有哦,如果#号位置不固定,你在1楼写的程序也是直接挂了哦!
这就是你弄了一大堆C++代码带来的 安全性可扩展性

我的C代码至少挪了位置没挂吧?

#31


引用 30 楼 gemo 的回复:
还有哦,如果#号位置不固定,你在1楼写的程序也是直接挂了哦!
这就是你弄了一大堆C++代码带来的 安全性可扩展性

我的C代码至少挪了位置没挂吧?

我2楼说过了,我没有检查楼主的逻辑,只是对一些编码习惯做修改而已
楼主会那样写,明显是想得到一个较具弹性地函数
你却拿apple比orange,提出一个限制多多的解法后再来说c++的特性没用?

所以我也学你来个apple比orange
直接cout就够了,你说我没救,其实是在说你自己啊
我们的行为都是apple比orange

#32


BOOL splitString(char *_Src, char **_Words, char **_Nums)
{
    if (_Src == NULL) return FALSE;
    if (_Words == NULL) return FALSE;
    if (_Nums == NULL) return FALSE;

    size_t len = ::strlen(_Src);
    char *word = new char[len + 1];
    char *num = new char[len + 1];

    ::memset(word, 0, len + 1);
    ::memset(num, 0, len + 1);

    *_Words = word;
    *_Nums = num;

    char *p = _Src;
    do
    {
        if (*p >= '0' && *p <= '9')
            *num++ = *p;
        else if (*p >= 'a' && *p <= 'z')
            *word++ = *p;
        else if (*p >= 'A' && *p <= 'Z')
            *word++ = *p;
    } while (*p++);

    return TRUE;
}


调用
char chSource[] = "#C255 244 233 12#   #d4#       #f5";
char *words, *nums;
splitString(chSource, &words, &nums);
::printf("%s\n", words);
::printf("%s\n", nums);
delete[] words;
delete[] nums;

#33


引用 31 楼 stereoMatching 的回复:
Quote: 引用 30 楼 gemo 的回复:

还有哦,如果#号位置不固定,你在1楼写的程序也是直接挂了哦!
这就是你弄了一大堆C++代码带来的 安全性可扩展性

我的C代码至少挪了位置没挂吧?

我2楼说过了,我没有检查楼主的逻辑,只是对一些编码习惯做修改而已
楼主会那样写,明显是想得到一个较具弹性地函数
你却拿apple比orange,提出一个限制多多的解法后再来说c++的特性没用?

所以我也学你来个apple比orange
直接cout就够了,你说我没救,其实是在说你自己啊
我们的行为都是apple比orange


我只知道如果#号位置不固定,你的代码挂了

#34


输出

Cdf
2552442331245

#35


代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

#36


如果楼主希望让程式更容易维护
可以考虑使用std::regex,这是c++11正式支援的regular expression
至于我之前提到的boost::spirit, 虽然大部分情况下性能比c提供的标准函数还要快
但是他需要你对c++有比较深入的了解和掌握,事后有兴趣再接触也可以

给楼主另外一个版本的实现,已经把template去掉,原版的设计是希望可以支援不同的"string"和"result"
不过楼主大概刚接触c++不久,所以我先介绍你比较简单的

void split(std::string const& contents, std::vector<std::string> &result, std::string const& delimiters = "\n")
{

    std::string::size_type pos, last_pos = 0;
    while(true)
    {
        pos = contents.find_first_of(delimiters, last_pos);
        if(pos == std::string::npos)
        {
            pos = contents.length();

            if(pos != last_pos)
                result.emplace_back(contents.data() + last_pos, pos - last_pos);

            break;
        }
        else
        {
            if(pos != last_pos)
                result.emplace_back(contents.data() + last_pos, pos - last_pos );
        }

        last_pos = pos + 1;
    }
}

void test_stl()
{
    std::cout<<"get number"<<std::endl;
    std::vector<std::string> strs;
    split("#C255 244 233 22##H5##a1#", strs, "#CHa ");
    for(auto const &data : strs){
        std::cout<<data<<std::endl;
    }
}


output
255
244
233
22
5
1

希望这是楼主想要的输出

我把result设计成parameters而非return by function
是为了让memory(std::vector<std::string>)可以重复利用
如果你需要更简洁的api,大可以改成return by function

#37


引用 35 楼 gemo 的回复:
代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

你的代码改点输入不是一样会挂。#号多写几个的情况下。

#38


简单的示范一下怎么截取char


std::cout<<"get number"<<std::endl;
    std::vector<std::string> strs;
    split("#C25#5# 244 233 22##H5##a1#", strs, "#CHa ");
    for(auto const &data : strs){
        std::cout<<data<<std::endl;
    }

    std::cout<<"get char"<<std::endl;
    strs.clear(); //this way the memory already allocated could be resued
    split("#C25#5# 244 233 22##H5##a1#", strs, "12345# ");
    for(auto const &data : strs){
        std::cout<<data<<std::endl;
    }

#39


Cpp库里没有这个函数(包括正则表达式)吗?

#40


我也很希望他有切割字串的函数,大部分情况下当然还是使用世界最顶尖的人开发出来的
东西会比较有保障,轻松,好维护
现在因为都在用QString,这方面的问题被简化了很多(QString有支援split)

至于正则表达式,c++11已经有支援了

#41


引用 37 楼 FancyMouse 的回复:
Quote: 引用 35 楼 gemo 的回复:

代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

你的代码改点输入不是一样会挂。#号多写几个的情况下。


能不能告诉下要多些几个#号的情况下会挂?我试了没问题呢?
C++字符串解析分享,求优化

#42


引用 37 楼 FancyMouse 的回复:
Quote: 引用 35 楼 gemo 的回复:

代码改点输入参数就会挂,还要大谈各种特性,难怪Linus要骂C++

你的代码改点输入不是一样会挂。#号多写几个的情况下。

不要跟猪玩

#43


大家来看看LS的素质

#44


这明明可以用正规文法描述的玩意, 怎么有人还在扯用基于上下文无关文法的spirit的有可能更快,无关的规约那么多,不慢个两三倍就看得起它了, 有这样扯淡的么...

听大妈的用正则表达式就可以了, 当然转成DFA的方式会更好.

#45


谢谢stereoMatching的讲解和修改,我的需求确实是这样的,#这个符号 是不确定的,可能会是  @ ¥%等等吧,除了##之间肯定会有字母 数字之外,数字的多少 ,位置 其实没固定。

其实我接触C++一段时间了,对语言中的细节接触还是太少,谢谢各位啦哈, 准备结贴,下个问题继续讨论string

#46


引用 44 楼 mLee79 的回复:
怎么有人还在扯用基于上下文无关文法的spirit的有可能更快,无关的规约那么多,不慢个两三倍就看得起它了, 有这样扯淡的么...

spirit2的主要优点有两个,一个是快(公认的,benchmark就摆在眼前,而且spirit3会比现在更快);一个是类似EBNF的语法可以让程式容易维护。
如果他不够快,我相信不会有什么人想使用这东西,他的学习门槛和编译时间不值得我们去承受

缺点 : 
由于非常依赖编译器的运算,编译速度极慢;
编译错误难以解读,建议从后面读起(backtrace);
各种type的宣告非常麻烦


std::string target("#C25#5# 244 233 22##H5##a1#");
    target.reserve(target.size() * 15);
    for(size_t i = 0; i != 15; ++i){
        target += target;
        std::cout<<i<<std::endl;
    }
    {
        timeEstimate<> time;
        //std::cout<<"get number"<<std::endl;
        std::vector<std::string> strs;
        for(size_t i = 0; i != 100; ++i){
            split(target, strs, "#CHa "); //我在第36楼的实现
            strs.clear();
        }
        //for(auto const &data : strs){
        //    std::cout<<data<<std::endl;
        //}
    }

    {
        using boost::spirit::qi::double_;
        using boost::spirit::qi::char_;
        using boost::spirit::qi::_1;
        using boost::spirit::qi::phrase_parse;
        using boost::spirit::ascii::space;
        using boost::phoenix::push_back;
        using boost::phoenix::ref;

        {
            timeEstimate<> time;
            std::vector<int> v;
            for(size_t i = 0; i != 100; ++i){
                phrase_parse(target.cbegin(), target.cend(),
                             (
                                 *( *char_("#CHa")>> double_[push_back(boost::phoenix::ref(v), _1)])
                             //double_[push_back(boost::phoenix::ref(v), _1)]) % "#"
                             )
                        ,
                        space);
                v.clear();
            }

        }

    }


output
Elapsed time : 1.059
Elapsed time : 0.551

引用 44 楼 mLee79 的回复:
听大妈的用正则表达式就可以了, 当然转成DFA的方式会更好.

确实用正则就好了,

#47


编译原理是门很成熟的玩意了, 没有spirit的几十年前大家就直接写EBNF自动生成代码了, 那东西没啥意思, 速度也不会比第三方的LALR, LL parser跑的更快 ..

能用有穷自动机描述的东西就不要用下推自动机去做, 性能就不是一个档次的, 同样的文法, PDA比 DFA只慢了2,3倍就算人品好了, 并且有穷自动机代码简单, 容易理解 ..

#48


引用 47 楼 mLee79 的回复:
编译原理是门很成熟的玩意了, 没有spirit的几十年前大家就直接写EBNF自动生成代码了, 那东西没啥意思, 速度也不会比第三方的LALR, LL parser跑的更快 ..

能用有穷自动机描述的东西就不要用下推自动机去做, 性能就不是一个档次的, 同样的文法, PDA比 DFA只慢了2,3倍就算人品好了, 并且有穷自动机代码简单, 容易理解 ..

我忘了另外一个好处,就是你可以直接在c++中使用
好歹他的速度还是比regular expression快
并且也比大部分水准普通(例如我)的人写的代码快
甚至比c的函数还要更快

你说的很有道理,但不是所有人都想用LALR, LL parser之类的工具写parser的
spirit至少提供了另外一个选择,有兴趣的人自然会去学习他
其实spirit这东西我是很久没碰了,我不需要什么性能很高的parser
一般的regular expression就能满足我的需求
不过spirit3出现时我应该还是会下来玩一玩的
spirit3的语法比起spirit2是简洁了不少,性能也提高了不少

#49


#50


ls来秀一把你的代码