如何迭代由空格分隔的单词组成的字符串中的单词?
注意,我对C字符串函数或那种字符操作/访问不感兴趣。比起效率,我更喜欢优雅。我当前的解决方案:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
string s = "Somewhere down the road";
istringstream iss(s);
do {
string subs;
iss >> subs;
cout << "Substring: " << subs << endl;
} while (iss);
}
虽然有一些答案提供了C++20解决方案,但自从发布以来,已经做了一些更改,并将其作为缺陷报告应用于C++20。正因为如此,解决方案变得更短、更好:
#include <iostream>
#include <ranges>
#include <string_view>
namespace views = std::views;
using str = std::string_view;
constexpr str text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
auto splitByWords(str input) {
return input
| views::split(' ')
| views::transform([](auto &&r) -> str {
return {r.begin(), r.end()};
});
}
auto main() -> int {
for (str &&word : splitByWords(text)) {
std::cout << word << '\n';
}
}
到今天为止,它仍然只在GCC的主干分支(Godbolt链接)上可用。它基于两个更改:P1391迭代器构造函数用于std::string_view和P2210 DR修复std::views::split以保留范围类型。
在C++23中,不需要任何转换样板,因为P1989向std::string_view:添加了一个范围构造函数
#include <iostream>
#include <ranges>
#include <string_view>
namespace views = std::views;
constexpr std::string_view text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
auto main() -> int {
for (std::string_view&& word : text | views::split(' ')) {
std::cout << word << '\n';
}
}
(螺栓连杆)
使用std::stringstream非常好,并且完全符合您的要求。如果您只是在寻找不同的方法,那么可以使用std::find()/std::find_first_of()和std::string::substr()。
下面是一个示例:
#include <iostream>
#include <string>
int main()
{
std::string s("Somewhere down the road");
std::string::size_type prev_pos = 0, pos = 0;
while( (pos = s.find(' ', pos)) != std::string::npos )
{
std::string substring( s.substr(prev_pos, pos-prev_pos) );
std::cout << substring << '\n';
prev_pos = ++pos;
}
std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
std::cout << substring << '\n';
return 0;
}
这里有一个只使用标准正则表达式库的简单解决方案
#include <regex>
#include <string>
#include <vector>
std::vector<string> Tokenize( const string str, const std::regex regex )
{
using namespace std;
std::vector<string> result;
sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
sregex_token_iterator reg_end;
for ( ; it != reg_end; ++it ) {
if ( !it->str().empty() ) //token could be empty:check
result.emplace_back( it->str() );
}
return result;
}
正则表达式参数允许检查多个参数(空格、逗号等)
我通常只选中空格和逗号分隔,所以我也有这个默认函数:
std::vector<string> TokenizeDefault( const string str )
{
using namespace std;
regex re( "[\\s,]+" );
return Tokenize( str, re );
}
“[\\s,]+”检查空格(\\s)和逗号(,)。
注意,如果要拆分wstring而不是string,
将所有std::regex更改为std::wregex将所有sregex_token_iterator更改为wsregex_token_idterator
注意,根据编译器的不同,您可能还希望引用字符串参数。
使用vector作为基类的快速版本,可完全访问其所有运算符:
// Split string into parts.
class Split : public std::vector<std::string>
{
public:
Split(const std::string& str, char* delimList)
{
size_t lastPos = 0;
size_t pos = str.find_first_of(delimList);
while (pos != std::string::npos)
{
if (pos != lastPos)
push_back(str.substr(lastPos, pos-lastPos));
lastPos = pos + 1;
pos = str.find_first_of(delimList, lastPos);
}
if (lastPos < str.length())
push_back(str.substr(lastPos, pos-lastPos));
}
};
用于填充STL集的示例:
std::set<std::string> words;
Split split("Hello,World", ",");
words.insert(split.begin(), split.end());
我编写了以下代码。您可以指定分隔符,它可以是字符串。结果类似于Java的String.split,结果中包含空字符串。
例如,如果我们调用split(“ABCPICKABCANYABCTWO:ABC”,“ABC”),结果如下:
0 <len:0>
1 PICK <len:4>
2 ANY <len:3>
3 TWO: <len:4>
4 <len:0>
代码:
vector <string> split(const string& str, const string& delimiter = " ") {
vector <string> tokens;
string::size_type lastPos = 0;
string::size_type pos = str.find(delimiter, lastPos);
while (string::npos != pos) {
// Found a token, add it to the vector.
cout << str.substr(lastPos, pos - lastPos) << endl;
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = pos + delimiter.size();
pos = str.find(delimiter, lastPos);
}
tokens.push_back(str.substr(lastPos, str.size() - lastPos));
return tokens;
}