带有两个分隔符的stringstream

时间:2018-03-10 09:11:40

标签: 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获取的第一个字符串然后用第二个分隔符分隔,并且它的子字符串通常被添加到向量中,但看起来第一个字符串停止并且没有传递其余的字符串。 是什么导致了这种行为?该函数的第一次何时以及如何破坏?

1 个答案:

答案 0 :(得分:1)

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

<强> Utility.h

#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

<强> Utlity.cpp

#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 。我个人认为没什么大不了的,但是如果关注的话你可以删除包装类工具并将所有常用的独立函数放在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表示:

  

&#34;没有创建过Util的对象;它有点像命名空间。&#34; - 然后使用实际的命名空间。正如我所说,这不是Java。

我会反对。别弄错我的意思;命名空间是C ++的一个重要且重要的部分,应该在适当的地方使用。但是,在某些情况下,命名空间是不够的,语言和编译器所需的机制需要使用类或结构的形式。这是什么意思?这是一个稍微高级的话题,但它很简单。你看,我上面提到的课程;我只展示了这门课程的几个功能。我有大约十几个其他功能,其中一些是功能模板。因此,关于在命名空间中使用单独的函数模板,可以说同样的论点。这不是我班级的情况。我的类有一些私有函数模板,用于将字符串作为输入并将其转换为默认的基本类型(int,unsigned,float,double等)。我甚至有将函数转换为glm::vec2vec3vec4glm::ivec2ivec3ivec4,{{1}的函数},glm::mat3这些遵循相同的惯例。

这些转换函数调用一个函数模板来获取该类型的值,但它依赖于函数模板特化;这不能在命名空间中完成。如果你试图在没有类或结构的情况下这样做;代码将编译但你会得到链接器错误,因为模板的参数列表不能接受命名空间;但它可以接受类,结构或整数类型。

这是一个伪示例:

glm::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) } 函数在所有情况下都将字符串作为参数,但唯一的区别是返回类型。在C ++和大多数其他语言中,无法单独对返回类型执行重载解析。它最终可能是一个模糊的调用,或者如果从未使用过返回类型;编译器甚至可能不会调用该函数开头。所以为了模拟这个;功能需要是模板类型;并且它们需要包含在类中以便专门化这些函数,否则您不能专门化仅仅在命名空间中的函数模板。

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

它与Java无关;坦率地说,我从来没有用Java编写任何东西。我只用C和C ++编程。多年前我在视觉基础和C#中做过几个很小的应用程序,当我90年代末在高中时,但在过去的20年里,它几乎是所有C ++的95%。你是对的,这是C ++,而不是Java。

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