c++ - 具有两个定界符的字符串流

标签 c++ delimiter stringstream

我试图获取一个字符串并先使用第一个定界符然后使用第二个定界符对其进行定界,我试图最小化使用的资源并避免不必要的循环(然后我不确定可能需要多少个循环)。我也是相当新的C ++程序员。我当前使用的代码显示在以下功能中。

vector<string> dualDelimit(string str,char del1,char del2)
{
    vector<string> sub;
    stringstream ss(str);
    stringstream ss2;
    while(ss.good())
    {
        string substring;
        getline(ss,substring,del1);

        ss2<<substring;
        while(ss2.good())
        {
            getline(ss2,substring,del2);
            sub.push_back(substring);
        }
    }
    return sub;
}


所以我也使用以下方法获取输入,调用dualDelimit然后打印出其内容

void delimitTest()
{
    //variables
    string input;
    char c;
    char d;
    vector<string> result;

    //main program
    cout<<"Insert the string to be delimited: ";
    getline(cin,input);
    cout<<"Insert the delimiter characters separated by whitespace:\t";
    cin>>c>>d;


    result=dualDelimit(input,c,d);

    for(int i=0;i<result.size();i++)
    {
        cout<<result[i]<<endl;
    }
}


假设我使用以下数据:

Insert the string to be delimited: This|is;a|test.
Insert two delimiter characters separated by whitespace:    ; |


打印并存储在结果向量中的结果只有:

This
is


看来,从ss提取的第一个字符串然后用第二个定界符定界,并且其子字符串通常添加到向量中,但似乎第一个while停止并且不传递其余字符串。
是什么导致这种行为?函数的第一个while何时以及如何中断?

最佳答案

我已经使用了一段时间了,效果很好。

实用程序

#ifndef UTILITY_H
#define UTILITY_H

class Utility {
public:
    Utility() = delete;
    Utility( const Utility& ) = delete;
    Utility& operator=( const Utility& ) = delete;

    static std::string trim( const std::string& str, 
                             const std::string elementsToTrim = " \t\n\r" ); 

    static std::vector<std::string> splitString( const std::string& stringToSplit, 
                                                 const std::string& strDelimiter, 
                                                 const bool keepEmpty = true );    
};

#endif // !UTILITY_H


实用工具

#include "Utility.h"

#include <vector>
#include <string>
#include <algorithm>

std::string Utility::trim( const std::string& str, 
                          const std::string elementsToTrim ) {
    std::basic_string<char>::size_type firstIndex = str.find_first_not_of( elementsToTrim );
    if ( firstIndex == std::string::npos )
        return std::string(); // Nothing Left    
    std::basic_string<char>::size_type lastIndex = str.find_last_not_of( elementsToTrim );
    return str.substr( firstIndex, lastIndex - firstIndex + 1 );
}

std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, 
                                               const std::string& strDelimiter, 
                                               const bool keepEmpty ) {
    std::vector<std::string> vResult;
    if ( strDelimiter.empty() ) {
        vResult.push_back( strStringToSplit );
        return vResult;
    }    
    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
    while ( true ) {
        itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
        std::string strTemp( itSubStrStart, itSubStrEnd );
        if ( keepEmpty || !strTemp.empty() )
            vResult.push_back( strTemp );
        if ( itSubStrEnd == strStringToSplit.end() )
            break;
        itSubStrStart = itSubStrEnd + strDelimiter.size();
    }    
    return vResult;    
}


main.cpp

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

#include "Utility.h"

int main() {
    std::vector<std::tring> result;
    std::string str( "This|is;a|test." );
    std::cout << str << std::endl;

    result = Utility::splitString( str, ";" );

    str.clear();
    for ( auto& s : result )
        str += s + " ";
    std::cout << str << std::endl;

    result.clear();
    result = Utility::splitString( str, "|" );

    str.clear();
    for ( auto& s : result )
        str += s + " ";
    std::cout << str << std::endl;

    system( "PAUSE" );
    return 0;
}




这个splitString函数之所以如此出色,是因为我有一个字符串模式,并且我想这样消除字符串中的字符集;您可以在上面的主体中重复使用相同的向量和字符串,然后运行以下命令:

{
    // [...]

    str.clear();
    result.clear();

    str = std::string( "cruelcruelhellocruelcruelmadryochcruel" );
    result = Utility::splitString( str,  "cruel" );
    str.clear();
    for ( auto& s : result )
        str += s + " ";
    str = Utility::trim( str );
    std::cout << str << std::endl;

    system( "PAUSE" );
    return 0;
}


它可以消除字符串中的字符集或字符串集。快速示例-结果。

test string-wellaaabbbdone

splitString( s, "a" )   = "well   bbbdone"
               "aa"     = "well abbbdone"
               "aaa"    = "well bbbdone";
               "ab"     = "wellaa bbdone";      
               "aabb"   = "wella bdone";
               "aaabbb" = "well done";
// without calling trim.* This may not be accurate; I did add it & removed it
// several times; but you should still get the idea.


您可以轻松地将stringstream替换为此splitString()算法,并仅在需要时使用其str()函数。



编辑-用户:Christian Hackl抱怨我在类中包含所有相关的静态函数,并说使用名称空间代替,因为这是C ++而不是Java。我个人不知道有什么大不了的,但是如果您担心,您可以删除包装类Utility,然后将所有通常相关的独立函数放在namespace中,如下所示:

// Utility.h
#ifndef UTILITY_H
#define UTILITY_H

namespace util {
    // function declarations;

} // namespace util 

#endif // !UTILITY_H 

//===================================

// Utility.cpp
#include "Utility.h"
// One of tree ways here:

// First:
using namespace util;

// function definitions;

// Second:
util::function definitions

// Third: 
namespace util {
    // function definitions
} // namespace util




最后说明:

在我上面的原始类Utility中,它包含用于处理字符串的静态方法,出于特定原因,我将它们放在类中,而不是仅仅驻留在名称空间中的独立函数。

用户– Christian Hackl说:


  “没有创建Util的对象;它有点像一个名称空间。” -然后使用实际的名称空间。如我所说,这不是Java。


我会反对。别弄错我的意思;名称空间是C ++的重要组成部分,应在适当的地方相应地使用。但是,在某些情况下,名称空间不足,并且语言和编译器所需的机制需要类或结构的形式。我是什么意思这是一个稍微高级的主题,但是非常简单。您可以在上面提供的课程中看到;我仅展示了该类的几个功能。我还有大约十二个其他功能,其中一些是功能模板。因此,关于在命名空间中放置一个单独的函数模板,可以说相同的论点。我班上的情况并非如此。我的课程有几个私有函数模板,这些模板用于将字符串作为输入并将其转换为默认的基本类型(int,unsigned,float,double等)。我什至拥有将字符串转换为glm::vec2vec3vec4glm::ivec2ivec3ivec4glm::mat3glm::mat4的函数,它们遵循相同的约定。

这些转换函数调用一个获取该类型值的函数模​​板,但它依赖于函数模板的专门化;这不能在名称空间中完成。如果您尝试在没有类或结构的情况下执行此操作;代码将编译,但由于模板的参数列表不能接受名称空间,因此会出现链接器错误;但它可以接受类,结构或整数类型。

这是一个伪示例:

{    // header file
     ...
     static int converToInt( const std::string& str );
     static float convertToFloat( const std::string& str );
private:
     template<typename T>
     bool hasValue( const std::string& str, T* pValue );

     template<typename T>
     T getValue( const std::string );
}


// *.inl file
template<typename T>
bool Utility::hasValue( const std::string& str, T* pValue ) {
    // string manipulation
    pValue[0] = getValue<T>( str );

    // check some condition return true or false
}

// *.cpp file
int convertToInt( const std::string& str ) {
   // return converted int from string
}

float convertToFloat( const std::string& str ) {
   // return converted float from string
}

template<>
int getValue( const std::string& ) {
    return int that was converted from (convertToInt)
}

template<>
float getValue( const std::string& ) {
    return float that was converted from (convertToFloat)
}


您不能仅在名称空间中在类外部执行此操作。因此,这就是为什么我在上面的原始演示中的函数都是不可构造类中的静态方法。

之所以需要这种模式,是因为getValue()函数在所有情况下都将字符串作为其参数,但是唯一的区别是返回类型。在C ++和大多数其他语言中,不能仅对返回类型执行重载解析。它可能最终是一个模棱两可的调用,或者如果从不使用返回类型;编译器甚至可能根本不调用该函数。因此为了模拟这一点;函数必须是模板类型的地方;并且它们必须包含在一个类中才能专门化那些功能,否则您就不能专门化仅在名称空间中的功能模板。



关于this is C++而不是Java的全部评论确实是一个错误的评估。为什么?不要以任何形式贬低用户,但声明似乎完全有偏见。第一;将静态方法放入没有任何成员的结构或类中并使构造函数私有化是完全合法和有效的C ++代码。

它与Java无关。坦率地说,我从未用Java编写过任何东西。我只用C和C ++编程。早在90年代末我上高中的时候,我就已经在视觉基础和C#上做过一些小小的应用程序,但是在过去的20年中,几乎所有的C ++都达到了95%。没错,这是C ++,而不是Java。

欢迎来到具有多态行为,重复发生的模板,模板元编程,通用编程的模板编程世界,同时仍支持在静态类型语言中既是过程型还是面向对象范例的角色,该语言现在支持更丰富的功能,例如自动类型演绎,lambda,基于范围的循环,可变参数模板等等!

关于c++ - 具有两个定界符的字符串流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49207336/

相关文章:

c++ - 基于位置的包中的特定类型

c++ - 为什么 C 没有没有指针的地址/引用传递?

c++ - 好吧,要么我疯了,要么 IBM 的编译器有错误

r - 在 R 中写入 csv 文件时的分隔符

java - string.split 方法中的字符串分隔符

c++ - 在 C++ 中使用 sqlite3 时内存泄漏

C++ stringstream 和失败与 eof

c++ - 调整视频捕获的分辨率

c++ - 使用 eclipse C++ 在 OSX 上创建应用程序包

Python按长度切割句子