有没有人知道如何进行单程搜索&替换为文本?我正在开发一个高性能程序,每个微优化都很重要。以下示例说明了我目前的工作:
#include <iostream>
#include <string>
/*!
\brief Replaces all common character escape sequences with text representations
\note Some character seqences messes up the trace output and must be replaces
* with text represantions, ie. the newline character will the replaced with "\n"
* etc.
\returns The formatted string
*/
std::wstring ReplaceAll(std::wstring &str)
{
SearchAndReplace(str, L"\a", L"\\a"); // control-g, C-g
SearchAndReplace(str, L"\b", L"\\b"); // backspace, <BS>, C-h
SearchAndReplace(str, L"\t", L"\\t"); // tab, <TAB>, C-i
SearchAndReplace(str, L"\n", L"\\n"); // newline, C-j
SearchAndReplace(str, L"\v", L"\\v"); // vertical tab, C-k
SearchAndReplace(str, L"\f", L"\\f"); // formfeed character, C-l
SearchAndReplace(str, L"\r", L"\\r"); // carriage return, <RET>, C-m
return str;
}
/*!
\brief Wide string search and replace
\param str [in, out] String to search & replace
\param oldStr [in] Old string
\param newStr [in] New string
*/
std::wstring SearchAndReplace(std::wstring &str, const wchar_t *oldStr, const wchar_t *newStr) const
{
size_t oldStrLen = wcslen(oldStr);
size_t newStrLen = wcslen(newStr);
size_t pos = 0;
while((pos = str.find(oldStr, pos)) != string::npos)
{
str.replace(pos, oldStrLen, newStr);
pos += newStrLen;
}
return str;
}
int main()
{
std::wstring myStr(L"\tThe quick brown fox jumps over the lazy dog.\n\tThe quick brown fox jumps over the lazy dog\n\n");
std::wcout << L"Before replace: " << myStr;
std::wcout << L"After replace: " << ReplaceAll(myStr);
return 0;
}
上面的代码显然效率低,因为它需要多次遍历同一个字符串。单通搜索&amp; replace函数应该非常灵活,它可以处理要替换的不同字符数组(即不仅仅是ReplaceAll()
中列出的转义字符。)
答案 0 :(得分:2)
您可以使用哈希表来存储<from,to>
的所有对,并在字符串上运行一次。
对于每个char都要检查它是否存在于哈希表中,如果存在则替换它。
它将一次性完成任务。
答案 1 :(得分:2)
对于手头的任务,您不需要任何复杂的算法!首先,您要搜索的“字符串”实际上是字符和不同的字符串(另一个答复中提到的更复杂的算法是处理与序列匹配的字符串列表) 。此外,您的主要问题是您不断调整序列大小。无论如何,你无法在原地进行替换,因为每次替换时弦都会增长。一个相当简单的方法应该具有很多比当前方法更好的性能,并且据我所知,你非常远离启动微优化 - 你需要首先让你的代码以正确的方式做事。例如,我会尝试这些行:
struct match_first
{
wchar_t d_c;
match_first(wchar_t c): d_c(c) {}
template <typename P>
bool operator()(P const& p) const { return p.first == this->d_c; }
};
void Replace(std::wstring& value)
{
std::wstring result;
result.reserve(value.size());
std::wstring special(L"\a\b\f\n\r\t\v");
std::pair<wchar_t, std::wstring> const replacements[] = {
std::pair<wchar_t, std::wstring>(L'\a', L"\\a"),
std::pair<wchar_t, std::wstring>(L'\b', L"\\b"),
std::pair<wchar_t, std::wstring>(L'\f', L"\\f"),
std::pair<wchar_t, std::wstring>(L'\n', L"\\n"),
std::pair<wchar_t, std::wstring>(L'\r', L"\\r"),
std::pair<wchar_t, std::wstring>(L'\t', L"\\t"),
std::pair<wchar_t, std::wstring>(L'\v', L"\\v")
};
std::wstring::size_type cur(0);
for (std::wstring::size_type found(cur);
std::wstring::npos != (found = value.find_first_of(special, cur));
cur = found + 1) {
result.insert(result.end(),
value.begin() + cur, value.begin() + found);
std::pair<wchar_t, std::wstring> const* replacement
= std::find_if(std::begin(replacements), std::end(replacements),
match_first(value[found]));
result.insert(result.end(),
replacement->second.begin(), replacement->second.end());
}
result.insert(result.end(), value.begin() + cur, value.end());
value.swap(result);
}
该算法的想法是对源字符串进行单次传递,找到需要替换的所有字符串,如果发现有一个不被替换字符的部分,则将替换字符串复制到新字符串建立。通过一些努力可以使得一些事情变得更快一些,但是这个只移动每个字符一次而不是原始代码,这使得字符的尾部不会被一个字符向前看,而每个字符都要被替换。< / p>
答案 2 :(得分:0)
有许多算法用于在线性时间内执行字符串搜索。即使它们中的大多数是字符串搜索算法,您也可以实现它们以在线性时间内执行字符串搜索和替换。它们的线性运行时需要一些预处理,请务必仔细阅读前提条件 very 。清单:
要使用这些字符串搜索算法来实现搜索和替换算法,您需要执行以下操作(请注意,我所做的分析专门针对KMP):
i
,检查该位置是否在您的hashmap中。如果是这样,找到相应的单词替换(存储在另一个散列映射中 - O(1)查找)并一次替换一个字符,直到j
- i
,其中{{1是你的下一个位置,大于你的替换的字符串长度减1,继续并重复,直到你迭代整个句子。 备注:对于第3步,您将逐个将字符复制到新字符串中。如果您找到了某个字词,则会复制该替换字词并跳过j
个位置,其中k
是匹配字词的字符串长度。
...最后:释放与原始字符串关联的任何内存,并返回新字符串或将指针设置为等于新字符串。
最后这应该是k
,因此是线性时间。
答案 3 :(得分:0)
搜索和替换中的主要问题是尝试就地执行它通常效率很低。创建一个新字符串是O(n)时间和空间;就地替换很难做到。
可以在字符串上进行两次传递;第一个只计算结果的长度(或者可能构造一个分散 - 收集列表)。完成后,如果需要,可以调整字符串的大小,然后可以完成替换传递,从字符串的末尾开始并朝着开头工作。然而,根据我的经验,这是很多编码的很少的价值。 (此外,如果某些替换是删除,它也不起作用。)
所以我使用类似下面的内容(使用C ++ 11 lambdas,只是因为它),虽然它不是最理想的:更好的方法是对替换向量进行排序以便可以使用二进制搜索,或者 - 在替换控制字符的情况下 - 将它们放入由目标字符索引的向量(具有最小值和最大值),以便查找只需要两次比较。 (或者你可以构建一个只需要一次比较的压缩表,但这也是很多工作。)
#include <algorithm>
#include <initializer_list>
#include <string>
#include <utility>
#include <vector>
template<typename Char, typename String=std::basic_string<Char>>
class Translator {
public:
Translator(const std::initializer_list<std::pair<Char, String>> trans) {
std::for_each(trans.begin(), trans.end(),
[&](const std::pair<Char, String>& fromto) {
from_.push_back(fromto.first);
to_.push_back(fromto.second);
});
}
void push_translated(String& res, Char ch) {
size_t pos = from_.find(ch);
if (pos == String::npos) res += ch; else res += to_[pos];
}
String translate(const String& orig) {
String rv;
std::for_each(orig.begin(), orig.end(), [&](Char ch){push_translated(rv, ch);});
return rv;
}
private:
String from_;
std::vector<String> to_;
};
上面的初始化列表的使用很可爱,但是也应该有一个构造函数,它需要std::map
或std::vector
对或类似的,因为有时候你会想要在运行时构建替换,而不是在编译时。
如果您想使用上面的代码,这里有一个简单的驱动程序(对于您的应用程序,您可能需要Translator<wchar_t>
):
#include <iostream>
int main(int argc, char** argv) {
Translator<char> trans{
{'\a', "\\a"},
{'\b', "\\b"},
{'\f', "\\f"},
{'\n', "\\n"},
{'\r', "\\r"},
{'\t', "\\t"},
{'\v', "\\v"}
};
for (int i = 1; i < argc; ++i) {
std::cout << trans.translate(argv[i]) << std::endl;
}
return 0;
}
答案 4 :(得分:0)
如果您正在寻找更好的性能,那么您的代码可能效率低下,因为这一行:
str.replace(pos,oldStrLen,newStr);
这可能导致:
一个愚蠢的STL实现甚至可以为每个替换动态分配缓冲区。 内存分配可能很慢,很容易变成瓶颈。
将输入和输出字符串/缓冲区分开,并将输出缓冲区预分配为大于输入缓冲区的字符串可能更有效。如果将输入和输出字符串分开,则可以保证程序将复制inputString.size()
个字符,并且只有一个初始内存分配(可预测的性能)。如果你就地替换字符,那么很可能不会移动任何字符并且不会发生重新分配,并且每次替换字符串中的每个字符都会被移动多次(更难以预测性能)和新的/ delete将被多次调用。
替换可以这样做:
reserve()
)。