C ++一次替换字符串中的多个字符串
Posted
技术标签:
【中文标题】C ++一次替换字符串中的多个字符串【英文标题】:C++ replace multiple strings in a string in a single pass 【发布时间】:2010-10-08 23:42:45 【问题描述】:给定以下字符串,"Hi ~+ and ^*. Is ^* still flying around ~+?"
我想用“Bobby”和“Danny”替换所有出现的"~+"
和"^*"
,所以字符串变成:
"Hi Bobby and Danny. Is Danny still flying around Bobby?"
我宁愿不必调用 Boost 替换函数两次来替换两个不同值的出现。
【问题讨论】:
字符串从何而来? 我们知道O(n) + O(n)
仍然是O(n)
,这里的真正动机是什么?
实际的字符串/数据可能有 100 GB 大小,增量处理,调用 replace 两次仍然是 O(n),n 相当大。
我正在尝试了解是否有可以立即使用的东西,而不是使用双重扫描。我希望有。感谢您回复我。
假设这不是一个练习,而是主要目标是处理可能有大约 100 个 GiG 的文本文件,替换用户指定的字符串,您是否考虑过“sed”?参见例如[linuxask.com/questions/replace-multiple-strings-using-sed]。你没有说任何关于行长的事情,这可能需要一些测试。
【参考方案1】:
我设法使用 Boost.iostreams 实现了所需的替换功能。具体来说,我使用的方法是使用正则表达式来匹配要替换的内容的过滤流。我不确定千兆字节大小的文件的性能。当然,您需要对其进行测试。无论如何,这是代码:
#include <boost/regex.hpp>
#include <boost/iostreams/filter/regex.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
int main()
using namespace boost::iostreams;
regex_filter filter1(boost::regex("~\\+"), "Bobby");
regex_filter filter2(boost::regex("\\^\\*"), "Danny");
filtering_ostream out;
out.push(filter1);
out.push(filter2);
out.push(std::cout);
out << "Hi ~+ and ^*. Is ^* still flying around ~+?" << std::endl;
// for file conversion, use this line instead:
//out << std::cin.rdbuf();
上面运行时打印"Hi Bobby and Danny. Is Danny still flying around Bobby?"
,就像预期的那样。
如果您决定对其进行衡量,看看性能结果会很有趣。
丹尼尔
编辑:我刚刚意识到regex_filter
需要将整个字符序列读入内存,这对于千兆字节大小的输入来说毫无用处。哦,好吧……
【讨论】:
【参考方案2】:我确实注意到它已经有一年了,但它是值得的。 I came across an article on CodeProject 今天声称可以解决这个问题 - 也许你可以使用那里的想法:
我不能保证它的正确性,但可能值得一看。 :)
该实现肯定需要将整个字符串保存在内存中,但您可以轻松解决这个问题(与执行替换的任何其他实现一样),只要您可以将输入拆分为块并保证您永远不会在在要被替换的符号的位置。 (在您的情况下,一种简单的方法是在下一个字符不是符号中使用的任何字符的位置进行拆分。)
--
除了性能之外还有一个原因(尽管在我的书中这是一个充分的理由)将“ReplaceMultiple”方法添加到一个字符串库中:简单地执行 N 次替换操作通常是不正确的。
如果替换符号的值不受约束,则 values 最终会在后续替换操作中被视为 symbols。 (在某些情况下您实际上想要这个,但肯定有您不想要的情况。使用看起来奇怪的符号会降低问题的严重性,但不能解决问题,并且“丑陋”,因为要格式化的字符串可能是用户可定义的 - 因此不需要外来字符。)
但是,我怀疑我无法轻松找到通用的多替换实现是有充分理由的。 “ReplaceMultiple”操作通常(显然)没有明确定义。
要了解这一点,请考虑“将 'aa' 替换为 '!' 可能意味着 '咩'和'?'在字符串“abaa”中?结果是'ab!'或“一个?” - 或者这样的替换是非法的?
可以要求符号“无前缀”,但在许多情况下这是不可接受的。假设我想用它来格式化一些模板文本。并说我的模板是用于代码的。我想用仅在运行时知道的数据库表名替换“§table”。如果我现在不能在同一个模板中使用“§t”,那会很烦人。模板化的脚本可能是完全通用的东西,而且你瞧,有一天我遇到的客户实际上在他的表名中使用了“§”……这可能会降低我的模板库的用处。
也许更好的解决方案是使用递归下降解析器,而不是简单地替换文字。 :)
【讨论】:
【参考方案3】:Boost string_algo 确实有一个 replace_all 功能。你可以使用它。
【讨论】:
谢谢 Matthew,但这只需要转换一个值,我需要调用它两次。我试图找出它有一种方法可以基本上给出一个地图,如果你发现 x1 替换为 y1 和 x2 替换为 y2 等等,并且对字符串的扫描只发生一次。【参考方案4】:我建议使用 Boost Format 库。而不是~+
和^*
,你可以使用%1%
和%2%
等等,更系统一点。
文档中的示例:
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
// prints "writing toto, x=40.230 : 50-th try"
干杯,
--阿尔夫
【讨论】:
感谢 Alf 的建议,但是我无法控制输入数据,因此您的建议将不起作用。我必须处理内容并更改调用代码的用户指定的值。【参考方案5】:我建议使用 std::map。所以你有一组替换,所以这样做:
std::map<std::string,std::string> replace;
replace["~+"]=Bobby;
replace["^*"]=Danny;
然后您可以将字符串放入字符串向量中并检查每个字符串是否出现在地图中,如果确实替换了它,您还需要从末尾删除任何标点符号。或者将它们添加到替换中。然后,您可以在一个循环中完成。我不确定这是否真的比 boost 更有效或更有用。
【讨论】:
以上是关于C ++一次替换字符串中的多个字符串的主要内容,如果未能解决你的问题,请参考以下文章
Java一次替换字符串中的多个不同子字符串(或以最有效的方式)