【问题标题】:How to split a string with two different delimiters [duplicate]如何用两个不同的分隔符分割字符串[重复]
【发布时间】:2020-01-26 17:38:17
【问题描述】:

我想将数据存储在对象数组中,但我不知道如何拆分字符串。

我想看到的结果是:

tab[0].username = "user1"
tab[0].ip = "192.168.0.1"
tab[1].username = "user2"
tab[1].ip = "192.168.0.2"
tab[2].username = "user3"
tab[2].ip = "192.168.0.3"

这是我的字符串的外观:

user1:192.168.0.1|user2:192.168.0.2|user3:192.168.0.3

我目前拥有的代码,只允许你在不管理管道的情况下进行拆分:

void addInTab(std::vector<std::string> vec, client *tab, int total_user)
{

    for(int i = 0; i < 2; ++i) {
        if (i == 0)
            tab[total_user].username = vec[i];
        if (i == 1)
            tab[total_user].ip = vec[i];
    }
}

void split(std::string str, char delim)
{
    std::vector<std::string> vec;
    std::string::size_type tmp = str.find(delim);

    while(tmp != std::string::npos) {
        vec.push_back(str.substr(0, tmp));
        str = str.substr(tmp + 1);
        tmp = str.find(delim);
    }
    vec.push_back(str);
    addInTab(vec);
}

提前感谢

【问题讨论】:

  • 你试过使用正则表达式吗?
  • 1) 请提供minimal reproducible example,因为您的示例无法编译。 2)如果你需要搜索多个分隔符,为什么不使用std::string::find_first_of
  • 你考虑过用 find_if 代替 find 吗?这将允许您使用自己的匹配检查功能并检查多个字符
  • 我会投票支持重复数据删除:这个问题更具体(更好地说明了实际问题),而目标问题太宽泛,不值得一提它吸引了选票。

标签: c++ string vector split substring


【解决方案1】:

只需拆分两次,第一次使用|,第二次(在每个结果上)使用: 作为分隔符。这是一个非常高效且紧凑的拆分函数

std::vector<std::string> split(const std::string& text, const char separator)
{
    std::vector<std::string> items;
    std::istringstream f(text);
    std::string s;
    while (getline(f, s, separator)) {
        items.push_back(s);
    }
    return items;
}

如果您确定关于分隔符交替这一事实,您可以通过交换分隔符来构建一个专门的函数,这里是一个简短的demo


#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>

int main()
{
    std::string text = "user1:192.168.0.1|user2:192.168.0.2|user3:192.168.0.3";
    std::vector<std::pair<std::string, std::string> > pairs;
    std::istringstream f(text);
    std::string name, ip;
    while (getline(f, name, ':')) {
        if (getline(f, ip, '|')) {
            pairs.push_back(std::pair<std::string,std::string>(name, ip));
        } else {
            break;
        }
    }

    for (auto pair: pairs) {
        std::cout << pair.first << ", " << pair.second << std::endl;
    }

}

【讨论】:

    【解决方案2】:

    您可以使用正则表达式来执行复杂的字符串处理。它通常比手动处理更容易维护和使用。

    #include <regex>
    #include <string>
    #include <iostream>
    #include <iterator>
    #include <algorithm>
    
    int main() {
        const std::string s = "user1:192.168.0.1|user2:192.168.0.2|user3:192.168.0.3";
        std::regex r(R"_((user\d+)\:((?:\d{1,3}\.?){4}))_");
    
        struct record {
            std::string username;
            std::string ip;
        };
        std::vector<record> records;
        std::transform(
            std::sregex_iterator{ s.begin(), s.end(), r }, std::sregex_iterator{},
            std::back_inserter(records),
            [](auto& sm) -> record { return { sm[1],sm[2] }; }
        );
    
        std::transform(
            records.begin(), records.end(),
            std::ostream_iterator<std::string>{std::cout, "\n"},
            [](auto& rec) { return rec.username + ':' + rec.ip; }
        );
    }
    

    【讨论】:

      【解决方案3】:

      如果你有

      你可以这样做:

      std::vector<std::string> tokens;
      boost::split(tokens, input, boost::is_any_of("|:."));    
      

      (那么只需将标记填充到您的结构中)


      Try it yourself !

      #include <boost/algorithm/string.hpp>
      #include <iostream>
      
      int main(){
          auto input = "user1:192.168.0.1|user2:192.168.0.2|user3:192.168.0.3";
      
          std::vector<std::string> tokens;
          boost::split(tokens, input, boost::is_any_of("|:."));    
      
          for (auto tok : tokens) {
            std::cout << tok << std::endl;
          }
      
          return 0;
      }
      

      如果你没有

      std::regex 可以做同样的事情:

      std::regex re("\\.;\\|");
      std::sregex_token_iterator first{input.begin(), input.end(), re, -1}, last;//the '-1' is what makes it split
      std::vector<std::string> tokens{first, last};
      

      【讨论】:

        【解决方案4】:

        你来了。

        #include <iostream>
        #include <string>
        #include <vector>
        
        std::vector<std::string> splitStr( const std::string &s, 
                                           const std::string &delim = " \t" )
        {
            std::vector<std::string> v;
        
            for ( std::string::size_type pos = 0, n = 0;
                  ( pos = s.find_first_not_of( delim, pos ) ) != std::string::npos; 
                  pos += n )
            {
                n = s.find_first_of( delim, pos );
        
                n = ( n == std::string::npos ? s.size() : n ) - pos;
        
                v.push_back( s.substr( pos, n ) );
            }
        
            return v;
        }
        
        int main() 
        {
            const std::string s( "user1:192.168.0.1|user2:192.168.0.2|user3:192.168.0.3 " );
            for ( const auto &item : splitStr( s, ":|" ) )
            {
                std::cout << item << std::endl;
            }
        
            return 0;
        }
        

        程序输出是

        user1
        192.168.0.1
        user2
        192.168.0.2
        user3
        192.168.0.3 
        

        也就是说你可以使用std::string类的搜索成员函数find_first_offind_first_not_of

        您可以为函数提供任何一组分隔符。

        【讨论】:

        • 这个解决方案的样板太多,不值得推荐 - 任何 都会比这更简单。
        • @darune 这是一个简单明了的解决方案,而且对初学者有用。
        • 您编写 split 函数的方式太“样板”了。这对初学者来说并不好。所以我想我只是不同意。
        • @darune 功能实现非常好。使用正则表达式会产生更多问题。至少这是这里提出的最好的建议。
        【解决方案5】:

        我建议您创建一个更通用的split 函数版本,它返回向量而不是调用一些特殊函数。

        然后你可以先调用它来分割管道字符,然后在循环中调用它再次来分割每个子字符串。

        类似的东西

        std::vector<std::string> split(std::string str, char delim);
        
        // ...
        
        for (auto pair : split(original_input_with_pipes, '|'))
        {
            // Here pair is a string containing values like "user1:192.168.0.1" etc.
        
            auto values = split(pair, ':');  // Split each pair
        
            // Now values[0] should be e.g. "user1"
            // and values[1] should be "192.168.0.1"
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-23
          • 1970-01-01
          • 2020-07-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-25
          相关资源
          最近更新 更多