【问题标题】:Split text with array of delimiters使用分隔符数组拆分文本
【发布时间】:2022-11-15 19:18:46
【问题描述】:

我想要一个按分隔符数组拆分文本的函数。我有一个完美运行的演示,但它真的很慢。这是参数的示例。

文本:

“pop-pap-bab 鲍勃”

分隔符向量:

“-”,“”

结果:

“pop”、“-”、“pap”、“-”、“bab”、“bob”

因此,函数循环抛出字符串并尝试查找分隔符,如果找到分隔符,则将找到的文本和分隔符推送到结果数组,如果文本仅包含空格或为空,则不推送文本.

std::string replace(std::string str,std::string old,std::string new_str){
    size_t pos = 0;
    while ((pos = str.find(old)) != std::string::npos) {
        str.replace(pos, old.length(), new_str);
    }
    return str;
}


std::vector<std::string> split_with_delimeter(std::string str,std::vector<std::string> delimeters){
    std::vector<std::string> result;
    std::string token;
    int flag = 0;
    for(int i=0;i<(int)str.size();i++){
        for(int j=0;j<(int)delimeters.size();j++){
            if(str.substr(i,delimeters.at(j).size()) == delimeters.at(j)){
                if(token != ""){
                    result.push_back(token);
                    token = "";
                }
                if(replace(delimeters.at(j)," ","") != ""){
                    result.push_back(delimeters.at(j));
                }
                i += delimeters.at(j).size()-1;
                flag = 1;
                break;
            }
        }
        if(flag == 0){token += str.at(i);}
        flag = 0;
    }
    if(token != ""){
        result.push_back(token);
    }
    return result;
}

我的问题是,函数真的很慢,因为它有 3 个循环。我想知道是否有人知道如何使该功能更快。对不起,如果我不够清楚,我的英语不是最好的。

【问题讨论】:

  • std::find_first_of。它不会自动更快,但可能会使您的代码更简单。你真的需要std::string分隔符吗?在您的示例中,它们都是单个字符
  • “真的很慢”有多慢?对于您的示例输入,即使是更高效的算法,我也不希望有太大差异
  • @463035818_is_not_a_number 它必须是字符串,因为在我的情况下有超过一个字符,有时它是 5 个或更多。当你有超过 100,000 个字符需要遍历时,它需要一分钟以上的时间来遍历
  • 不确定我是否理解你的代码。当您可以简单地调用 std::string::find 时,您似乎构造了子字符串以将它们与定界符进行比较。仅此一项就可能会导致加速,因为构造子字符串的成本很高。尽管在尝试手动优化之前,您是否打开了编译器优化?
  • @463035818_is_not_a_number 我不确定你所说的“打开编译器优化”是什么意思,我也不确定如何在函数中实现 std::string::find,请你帮我解决这个问题。我真的是编程新手

标签: c++


【解决方案1】:

也许,作为替代方案,您可以使用正则表达式?但也许对你来说也太慢了。 . .

使用正则表达式生活会非常简单。

请看下面的例子:

#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <iterator>

const std::regex re(R"((w+|[- ]))");

int main() {
    
    std::string s{"pop-pap-bab bob"};
    
    std::vector<std::string> part{std::sregex_token_iterator(s.begin(),s.end(),re),{}};
    
    for (const std::string& p : part)   std::cout << p << '
';
}

我们将std::sregex_token_iteratorstd::vectors 范围构造函数结合使用,以提取正则表达式中指定的所有内容,然后将所有这些内容放入std::vector

正则表达式本身也很简单。它指定单词或分隔符。

也许值得一试。 . .

【讨论】:

  • 该程序要快得多,但是您能否告诉我如何将字符串向量转换为正则表达式,因为我不确定如何将我的项目放入“std::regex re(R”((w+|[-])) ");"。例如我有 vector<string> items = {"==","!=",">=","<=","<",">","||","&&",}
【解决方案2】:

注意:您曾抱怨过您的代码速度很慢,但重要的是要了解大多数答案都有可能加快程序速度的选项。即使选项的作者测量了程序的加速,选项在您的机器上也可能更慢,所以不要忘记自己测量执行速度。

如果我是你,我会创建一个单独的函数来接收一个字符串数组并输出一个分隔字符串数组。这种做法的问题可能在于,如果分隔符中包含另一个分隔符,结果可能不是你所期望的,但通过different options for string splitting进行迭代会更容易,找到最好的。 我的解决方案看起来像这样(尽管它需要 c++20)

#include <iomanip>
#include <iostream>
#include <ranges>
#include <string_view>
#include <vector>

std::vector<std::string> split_elems_of_array(const std::vector<std::string>& array, const std::string& delim)
{
    std::vector<std::string> result;
    for(const auto str: array)
    {
        for (const auto word : std::views::split(str, delim))
        {
            std::string chunk(word.begin(), word.end());
            if(!chunk.empty() && chunk != " ")
                result.push_back(chunk + delim);
        }
    }

    return result;
}

std::vector<std::string> split_string(std::string str, std::vector<std::string> delims)
{
    std::vector<std::string> result = {std::string(str)};
    for(const auto&delim: delims)
        result = split_elems_of_array(result, delim);
    return {result.begin(), result.end()};
}

对于我的机器,我的方法快了 56 倍:67 毫秒对 5112 毫秒。字符串长度为 1000000,有 100 个长度为 100 的 delims

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 2018-09-10
    • 1970-01-01
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多