我读了The most elegant way to iterate the words of a string并且很享受答案的简洁。现在我想对string_view做同样的事情。问题是,stringstream
无法string_view
:
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
int main() {
using namespace std;
string_view sentence = "And I feel fine...";
istringstream iss(sentence); // <== error
copy(istream_iterator<string_view>(iss),
istream_iterator<string_view>(),
ostream_iterator<string_view>(cout, "\n"));
}
有没有办法做到这一点?如果不是,那么这样的事情不是惯用的原因是什么?
答案 0 :(得分:2)
如果您想使用该特定方法,则只需明确地将string_view
转换为string
:
istringstream iss{string(sentence)}; // N.B. braces to avoid most vexing parse
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
ostream_iterator<string_view>(cout, "\n"));
答案 1 :(得分:1)
stringstream
拥有它所操作的字符串。这意味着它会创建给定字符串的副本。它不仅可以引用字符串。
即使使用建议的基于string_view
的{{1}}类型,流仍然不是随机访问范围。他们没有办法处理字符串的子范围。这就是他们通过副本而不是通过迭代器或其他东西从流中提取数据的原因。
你想要的最好是通过一个基于stream
的机制完成,因为它可以在不复制任何东西的情况下工作。它们可以与regex
一起使用(尽管您必须手动构建string_view
)。
答案 2 :(得分:0)
由定界符分割并返回vector<string_view>
。
设计用于快速拆分.csv
文件中的行。
在{{1}必需的MSVC 2017 v15.9.6
和Intel Compiler v19.0
编译下进行测试(C++17
必需)。
string_view
请注意生命周期:#include <string_view>
std::vector<std::string_view> Split(const std::string_view str, const char delim = ',')
{
std::vector<std::string_view> result;
int indexCommaToLeftOfColumn = 0;
int indexCommaToRightOfColumn = -1;
for (int i=0;i<static_cast<int>(str.size());i++)
{
if (str[i] == delim)
{
indexCommaToLeftOfColumn = indexCommaToRightOfColumn;
indexCommaToRightOfColumn = i;
int index = indexCommaToLeftOfColumn + 1;
int length = indexCommaToRightOfColumn - index;
// Bounds checking can be omitted as logically, this code can never be invoked
// Try it: put a breakpoint here and run the unit tests.
/*if (index + length >= static_cast<int>(str.size()))
{
length--;
}
if (length < 0)
{
length = 0;
}*/
std::string_view column(str.data() + index, length);
result.push_back(column);
}
}
const std::string_view finalColumn(str.data() + indexCommaToRightOfColumn + 1, str.size() - indexCommaToRightOfColumn - 1);
result.push_back(finalColumn);
return result;
}
决不能超过父窗口string_view
,因为它是一个窗口。如果父级string
超出范围,则string
指向的内容无效。在这种特殊情况下,API设计很难出错,因为输入/输出都是string_view
,它们都是父字符串的所有窗口。最终在内存复制和CPU使用率方面非常有效。
请注意,如果使用string_view
,唯一的缺点就是丢失了隐式null终止。因此,请使用支持string_view
的函数,例如Boost中的string_view
函数可将字符串转换为数字。
我用它来快速解析.csv文件。为了获得.csv文件中的每一行,我使用了lexical_cast
和istringstream
,它们的速度非常快(单个内核约2GB /秒或每秒1,200,000行)。
单元测试。使用Google Test进行测试(我使用vcpkg安装)。
getLine()
答案 3 :(得分:0)
Contango 的回答很好。为了适应我的项目中的 string 和 boost::string_view,我做了一些改动,并尝试摆脱复制构造函数。
以下代码将字符串拆分为string_view;
你必须保证字符串不会被破坏。
还有其他答案可能在语法上更优雅:请查看:https://www.bfilipek.com/2018/07/string-view-perf-followup.html。 上面有一个istringstream的版本,如果字符串本身很长,复制会有点问题,需要自己处理。
typedef boost::string_view StringView; //Or you can just typedef std::string_view StringView;
#if defined(_WIN32) | defined(WIN32)
#pragma warning(push)
#pragma warning(disable:26486 26481)
#endif
void SplitStringToStringView(const std::string& str, const char delim, std::vector<StringView>* outputPointer)
{
if (outputPointer == nullptr)
return;
std::vector<StringView>& result = *outputPointer;
int indexCommaToLeftOfColumn = 0;
int indexCommaToRightOfColumn = -1;
const int end = boost::numeric_cast<int>(str.size());
for (int i = 0; i < end; i++)
{
if (str.at(i) == delim)
{
indexCommaToLeftOfColumn = indexCommaToRightOfColumn;
indexCommaToRightOfColumn = i;
const int index = indexCommaToLeftOfColumn + 1;
const int length = indexCommaToRightOfColumn - index;
// Bounds checking can be omitted as logically, this code can never be invoked
// Try it: put a breakpoint here and run the unit tests.
/*if (index + length >= static_cast<int>(str.size()))
{
length--;
}
if (length < 0)
{
length = 0;
}*/
result.emplace_back(StringView(str.c_str() + index, length));
}
}
const StringView finalColumn(str.c_str() + indexCommaToRightOfColumn + 1,
str.size() - indexCommaToRightOfColumn - 1);
result.push_back(finalColumn);
}
#if defined(_WIN32) | defined(WIN32)
#pragma warning(pop)
#endif
由于 Contango 提供了单元测试代码,非常好,我也应该这样做:
{
const std::string str = "A,B,C";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "A");
EXPECT_TRUE(tokens[1] == "B");
EXPECT_TRUE(tokens[2] == "C");
}
{
const std::string str = ",B,C";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "B");
EXPECT_TRUE(tokens[2] == "C");
}
{
const std::string str = "A,B,";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "A");
EXPECT_TRUE(tokens[1] == "B");
EXPECT_TRUE(tokens[2] == "");
}
{
const std::string str = "";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 1);
EXPECT_TRUE(tokens[0] == "");
}
{
const std::string str = "A";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 1);
EXPECT_TRUE(tokens[0] == "A");
}
{
const std::string str = ",";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 2);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "");
}
{
const std::string str = ",,";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 3);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "");
EXPECT_TRUE(tokens[2] == "");
}
{
const std::string str = "A,";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 2);
EXPECT_TRUE(tokens[0] == "A");
EXPECT_TRUE(tokens[1] == "");
}
{
const std::string str = ",B";
std::vector<StringView> tokens;
SplitStringToStringView(str, ',', &tokens);
EXPECT_TRUE(tokens.size() == 2);
EXPECT_TRUE(tokens[0] == "");
EXPECT_TRUE(tokens[1] == "B");
}