如何在不复制的情况下比较字符串的一部分?
How do I compare a section of a string without copying?
我有一个长字符串,我正在迭代,在每次迭代时,我将字符串的一部分与常量进行比较,并存储字符串的某些部分。在我的实际代码中,这段代码运行了数百万次,是主要的瓶颈。我认为这是由于过度使用std::string::substr
.
#include <iostream>
#include <map>
#include <string>
#include <vector>
int main() {
std::string str("0=My,1=comma,2=separated,3=string,0=with,3=repeated,7=IDs");
std::vector<std::string> out0;
std::map<std::string, std::string> out;
size_t pos = str.find(',');
// loop over the string, collecting "key=value" pairs
while (pos < str.size() - 1) {
if (str.substr(pos + 1, 2) == "0=") {
auto newPos = str.find(',', pos + 3);
out0.push_back(str.substr(pos + 3, newPos - pos - 3);
pos = newPos;
} else {
size_t eqPos = str.find('=', pos + 1);
auto newPos = str.find(',', eqPos + 1);
out[str.substr(pos + 1, eqPos - pos - 1)] = str.substr(eqPos + 1, newPos - eqPos - 1);
}
}
// print out the data structures (this doesn't happen in my actual code)
std::cout << "out0:";
for (auto& entry : out0) {
std::cout << ' ' << entry;
}
std::cout << std::endl;
std::cout << "out:";
for (auto it : out) {
std::cout << ' ' << it->first << '=' << it->second;
}
}
以下是我的问题:
- 如何在不执行复制和不为每个字符编写比较的情况下对字符串进行比较,例如
str[pos + 1] == '0' && str[pos + 2] == '=' && ...
? - 如何存储对子字符串的引用,而不是每次添加到
out0
和out
时都复制?
这可能是使用char *
的一个很好的例子,但我以前从未使用过它。
编辑:
不幸的是,我只有C++11;否则,std::string_view
是最好的答案。有没有办法在不std::string_view
的情况下完成引用的存储?
如果您有 C++17,则可以这样使用string_view
: (未经测试的代码(:
string_view sv{str.data() + pos, 2};
if (sv == "0=") ...
没有副本。甚至(一气呵成(:
if (string_view{str.data() + pos, 2} == "0=") ...
如果您没有string_view
,则可以使用char_traits
:
if (std::char_traits<char>::compare(str.data() + pos, "0=", 2) == 0) ...
由于人们已经发布了std::string_view,这里是普通的旧C指针版本。
(虽然没有测试,但它会给你想法(
见下文:
std::string str("0=My,1=comma,2=separated,3=string,0=with,3=repeated,7=IDs");
std::string substr("test");
.
. Inside some function
.
const char *str_p = str.c_str(); // String you want to compare with a substring
const char *substr_p = substr.c_str(); // Your substring
size_t str_len = str.length();
size_t substr_len = substr.length();
bool comparison_result = true;
for(size_t i = 0; i < str_len - substr_len; i++) {
for(size_t j = 0; j < substr_len; j++) {
if(*(str_p + i + j) != *(substr_p + j)) {
comparison_result = false;
break;
}
if (j == substr_len - 1) { // We can only reach here when substring is hit
comparison_result = true;
i = str_len - substr_len;
break;
}
}
}
return comparison_result;
编辑:
由于@Toby Speight在评论中的建议(我觉得非常好(,我也在实现一个std::memcmp((版本。在这种情况下,内部循环变为:
.
. Inside some function
.
const char *str_p = str.c_str(); // String you want to compare with a substring
const char *substr_p = substr.c_str(); // Your substring
size_t str_len = str.length();
size_t substr_len = substr.length();
bool comparison_result = false;
for(size_t i = 0; i < str_len - substr_len; i++) {
if(std::memcmp(str_p + i, substr_p, substr_len) == 0) {
comparison_result = true;
break;
}
}
return comparison_result;
编辑:
我们又接到了一个请求,这次是张@Alexander,我们来实现一下:
.
. Inside some function
.
const char *str_p = str.c_str(); // String you want to compare with a substring
const char *substr_p = substr.c_str(); // Your substring
size_t str_len = str.length();
size_t substr_len = substr.length();
bool comparison_result = false;
for(size_t i = 0; i < str_len - substr_len; i++) {
if(std::memcmp(&str_p[i], &substr_p[0], substr_len) == 0) {
comparison_result = true;
break;
}
}
return comparison_result;
对 out
的键和值使用 std::string_view
而不是 std::string
。 std::string_view
包含指向字符串的指针和字符串的大小,因此它的重量非常轻。 这使您可以提取所需的信息,但不必复制字符串中的任何字符以及创建这些字符串的任何潜在内存分配。
您需要做的是从std::string
获取string_view
,然后使用该string_view
获取所需的所有子字符串。
std::string
有compare()
个方法,这些方法将const char*
子字符串作为输入。 你不需要使用 std::string::substr()
来比较子字符串,例如:
#include <iostream>
#include <map>
#include <string>
#include <vector>
int main() {
std::string str("0=My,1=comma,2=separated,3=string,0=with,3=repeated,7=IDs");
std::vector<std::string> out0;
std::map<std::string, std::string> out;
size_t startPos = 0, delimPos, nameStart, nameEnd, valueStart, valueEnd;
// loop over the string, collecting "key=value" pairs
while (startPos < str.size()){
nameStart = startPos;
delimPos = str.find_first_of("=,", startPos, 2);
if (delimPos == std::string::npos) {
nameEnd = valueStart = valueEnd = str.size();
}
else {
nameEnd = delimPos;
if (str[delimPos] == '=') {
valueStart = nameEnd + 1;
valueEnd = str.find(',', valueStart);
if (valueEnd == std::string::npos) {
valueEnd = str.size();
}
}
else {
valueStart = valueEnd = nameEnd;
}
}
// TODO: if needed, adjust name(Start|End) and value(Start|End) to
// ignore leading/trailing whitespace around the name and value
// substrings...
if (str.compare(nameStart, nameEnd - nameStart, "0", 1) == 0) {
out0.push_back(str.substr(valueStart, valueEnd - valueStart));
} else {
out[str.substr(nameStart, nameEnd - nameStart)] = str.substr(valueStart, valueEnd - valueStart);
}
startPos = valueEnd + 1;
}
// print out the data structures
std::cout << "out0:";
for (auto& entry : out0) {
std::cout << ' ' << entry;
}
std::cout << std::endl;
std::cout << "out:";
for (auto it : out) {
std::cout << ' ' << it->first << '=' << it->second;
}
}
输出:
out0:我的 输出:1=逗号 2=分隔 3=重复 7=ID
现场演示
您可以更进一步,通过根本不在std::vector
和std::map
中存储std::string
值来完全消除substr()
的使用,而是存储std::pair<char*, size_t>
:
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <utility>
using StrView = std::pair<const char*, size_t>;
StrView makeStrView(const char *str, size_t size) {
return std::make_pair(str, size);
}
struct compareStrView {
bool operator()(const StrView &lhs, const StrView &rhs) const {
if (lhs.second == rhs.second)
return (std::char_traits<char>::compare(lhs.first, rhs.first, lhs.second) < 0);
return (lhs.second < rhs.second);
}
};
std::ostream& operator<<(std::ostream &os, const StrView &rhs) {
return os.write(rhs.first, rhs.second);
}
int main() {
std::string str("0=My,1=comma,2=separated,3=string,0=with,3=repeated,7=IDs");
std::vector<StrView> out0;
std::map<StrView, StrView, compareStrView> out;
size_t startPos = 0, delimPos, nameStart, nameEnd, valueStart, valueEnd;
// loop over the string, collecting "key=value" pairs
while (startPos < str.size()){
nameStart = startPos;
delimPos = str.find_first_of("=,", startPos, 2);
if (delimPos == std::string::npos) {
nameEnd = valueStart = valueEnd = str.size();
}
else {
nameEnd = delimPos;
if (str[delimPos] == '=') {
valueStart = nameEnd + 1;
valueEnd = str.find(',', valueStart);
if (valueEnd == std::string::npos) {
valueEnd = str.size();
}
}
else {
valueStart = valueEnd = nameEnd;
}
}
// TODO: if needed, adjust nameStart/End and valueStartEnd to
// ignore leading/trailing whitespace around the name and value
// substrings...
if (str.compare(nameStart, nameEnd - nameStart, "0", 1) == 0) {
out0.push_back(makeStrView(&str[valueStart], valueEnd - valueStart));
} else {
out[makeStrView(&str[nameStart], nameEnd - nameStart)] = makeStrView(&str[valueStart], valueEnd - valueStart);
}
startPos = valueEnd + 1;
}
// print out the data structures
std::cout << "out0:";
for (auto& entry : out0) {
std::cout << ' ' << entry;
}
std::cout << std::endl;
std::cout << "out:";
for (auto &it : out) {
std::cout << ' ' << it.first << '=' << it.second;
}
}
输出:
out0:我的 输出:1=逗号 2=分隔 3=重复 7=ID
现场演示
在 C++17 中,您可以改用std::string_view
:
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <string_view>
int main() {
std::string str("0=My,1=comma,2=separated,3=string,0=with,3=repeated,7=IDs");
std::string_view sv(str);
std::vector<std::string_view> out0;
std::map<std::string_view, std::string_view> out;
size_t startPos = 0, delimPos, nameStart, nameEnd, valueStart, valueEnd;
// loop over the string, collecting "key=value" pairs
while (startPos < sv.size()){
nameStart = startPos;
delimPos = sv.find_first_of("=,", startPos, 2);
if (delimPos == std::string_view::npos) {
nameEnd = valueStart = valueEnd = sv.size();
}
else {
nameEnd = delimPos;
if (sv[delimPos] == '=') {
valueStart = nameEnd + 1;
valueEnd = sv.find(',', valueStart);
if (valueEnd == std::string_view::npos) {
valueEnd = sv.size();
}
}
else {
valueStart = valueEnd = nameEnd;
}
}
// TODO: if needed, adjust nameStart/End and valueStartEnd to
// ignore leading/trailing whitespace around the name and value
// substrings...
if (sv.compare(nameStart, nameEnd - nameStart, "0", 1) == 0) {
out0.push_back(sv.substr(valueStart, valueEnd - valueStart));
} else {
out[sv.substr(nameStart, nameEnd - nameStart)] = sv.substr(valueStart, valueEnd - valueStart);
}
startPos = valueEnd + 1;
}
// print out the data structures
std::cout << "out0:";
for (auto& entry : out0) {
std::cout << ' ' << entry;
}
std::cout << std::endl;
std::cout << "out:";
for (auto &it : out) {
std::cout << ' ' << it.first << '=' << it.second;
}
}
您可以尝试使用正则表达式来拆分值对元组。
虽然还没有测试过是否有更快的速度
这个表达式应该可以解决问题,只需获取所有匹配项(所有对(
(?:(\d(+=(?:([^,]*(,?((*?
https://regex101.com/r/PDZMq0/1
- 视觉C++使用 map 来比较字符串中的每个单词
- 我正在尝试从输入文件中读取,然后使用它们的子字符串比较字符串的特定部分
- 比较字符串的 GetLine 工作一次,然后比较之后不起作用
- C++使用关系运算符比较字符串
- 通过默认复制构造函数比较 C++ 字符串是否会影响性能,原因为何?
- C++ TCP 服务器比较字符串失败
- 如何在不复制的情况下比较字符串的一部分?
- 比较 C++ 字符串打印中的两个整数数组
- 如何比较字符串形式的临时变量
- std::out_of_range 比较字符串中的符号时出错
- 比较字符串大小写不区分的简单方法是什么?
- 比较字符串忽略 NUL
- 比较字符串(arduino)时出现问题
- 如何在C++中比较字符串
- 比较字符串对象等于向量中的元素
- C 比较字符串操作速度
- 需要比较字符串的某些元素
- 比较字符串 .c_str() 和普通字符串的差异
- C 比较字符串的字符
- 通过使用两个不同的队列比较字符串