我知道这是一个非常简单的问题,但我只是想为自己一劳永逸地解决这个问题
我只想使用字符作为拆分分隔符将字符串拆分为数组。 (很像C#着名的 .Split()功能。我当然可以应用蛮力方法,但我想知道是否还有更好的方法。
到目前为止,我已经搜索过,可能最近的解决方案方法是使用 strtok(),但是由于它的不便(将您的字符串转换为字符串)数组等)我不喜欢用它。有没有更简单的方法来实现这个?
注意:我想强调这一点,因为人们可能会问“蛮力怎么行不通”。我的强力解决方案是创建一个循环,并使用里面的 substr()函数。但是,由于它需要起点和长度,因此当我想分割日期时它会失败。因为用户可能会在7/12/2012或07/3/2011输入它,在计算“/”分隔符的下一个位置之前,我可以确切地说出长度。
答案 0 :(得分:75)
使用向量,字符串和字符串流。有点麻烦,但它可以解决问题。
std::stringstream test("this_is_a_test_string");
std::string segment;
std::vector<std::string> seglist;
while(std::getline(test, segment, '_'))
{
seglist.push_back(segment);
}
导致矢量与
具有相同的内容std::vector<std::string> seglist{ "this", "is", "a", "test", "string" };
答案 1 :(得分:12)
喜欢RegEx的人的另一种方式(C ++ 11 / boost)。就我个人而言,我是这种数据的忠实粉丝。 IMO它比使用分隔符简单地分割字符串要强大得多,因为如果您愿意,您可以选择更加智能地构建“有效”数据。
#include <string>
#include <algorithm> // copy
#include <iterator> // back_inserter
#include <regex> // regex, sregex_token_iterator
#include <vector>
int main()
{
std::string str = "08/04/2012";
std::vector<std::string> tokens;
std::regex re("\\d+");
//start/end points of tokens in str
std::sregex_token_iterator
begin(str.begin(), str.end(), re),
end;
std::copy(begin, end, std::back_inserter(tokens));
}
答案 2 :(得分:10)
Boost在algorithm/string.hpp
中寻找 split():
std::string sample = "07/3/2011";
std::vector<string> strs;
boost::split(strs, sample, boost::is_any_of("/"));
答案 3 :(得分:4)
另一种可能性是使用使用特殊ctype
方面的区域设置来添加流。流使用ctype facet来确定它被视为分隔符的&#34;&#34; whitespace&#34;。使用ctype facet将分隔符分类为空格,读取可能非常简单。这是实现方面的一种方法:
struct field_reader: std::ctype<char> {
field_reader(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(table_size, std::ctype_base::mask());
// we'll assume dates are either a/b/c or a-b-c:
rc['/'] = std::ctype_base::space;
rc['-'] = std::ctype_base::space;
return &rc[0];
}
};
我们使用imbue
来告诉流使用包含它的语言环境,然后从该流中读取数据:
std::istringstream in("07/3/2011");
in.imbue(std::locale(std::locale(), new field_reader);
有了这个,分裂几乎变得微不足道 - 只需使用几个istream_iterator
来初始化一个向量来读取字符串中的片段(istringstream
中嵌入的片段):
std::vector<std::string>((std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>());
如果你只在一个地方使用它,这显然会导致过度杀伤。但是,如果你使用它很多,它可以很长时间地保持代码的其余部分非常干净。
答案 4 :(得分:2)
如果您想要汇总自己的方法,可以使用std::string::find()
来确定分裂点。
答案 5 :(得分:2)
我天生不喜欢stringstream
,虽然我不确定为什么。今天,我编写了这个函数,允许将std::string
任意字符或字符串拆分成一个向量。我知道这个问题已经过时了,但我想分享另一种分割方式std::string
。
此代码省略了您从结果中拆分的字符串部分,尽管可以轻松修改它以包含它们。
#include <string>
#include <vector>
void split(std::string str, std::string splitBy, std::vector<std::string>& tokens)
{
/* Store the original string in the array, so we can loop the rest
* of the algorithm. */
tokens.push_back(str);
// Store the split index in a 'size_t' (unsigned integer) type.
size_t splitAt;
// Store the size of what we're splicing out.
size_t splitLen = splitBy.size();
// Create a string for temporarily storing the fragment we're processing.
std::string frag;
// Loop infinitely - break is internal.
while(true)
{
/* Store the last string in the vector, which is the only logical
* candidate for processing. */
frag = tokens.back();
/* The index where the split is. */
splitAt = frag.find(splitBy);
// If we didn't find a new split point...
if(splitAt == string::npos)
{
// Break the loop and (implicitly) return.
break;
}
/* Put everything from the left side of the split where the string
* being processed used to be. */
tokens.back() = frag.substr(0, splitAt);
/* Push everything from the right side of the split to the next empty
* index in the vector. */
tokens.push_back(frag.substr(splitAt+splitLen, frag.size()-(splitAt+splitLen)));
}
}
要使用,只需这样打电话......
std::string foo = "This is some string I want to split by spaces.";
std::vector<std::string> results;
split(foo, " ", results);
您现在可以随意访问矢量中的所有结果。很简单 - 没有stringstream
,没有第三方库,没有回退到C!
答案 6 :(得分:2)
由于尚未有人发布此内容:c++20 解决方案使用 ranges
非常简单。您可以使用 std::ranges::views::split
拆分输入,然后将输入转换为 std::string
或 std::string_view
元素。
#include <ranges>
...
// The input to transform
const auto str = std::string{"Hello World"};
// Function to transform a range into a std::string
// Replace this with 'std::string_view' to make it a view instead.
auto to_string = [](auto&& r) -> std::string {
const auto data = &*r.begin();
const auto size = static_cast<std::size_t>(std::ranges::distance(r));
return std::string{data, size};
};
const auto range = str |
std::ranges::views::split(' ') |
std::ranges::views::transform(to_string);
for (auto&& token : str | range) {
// each 'token' is the split string
}
这种方法实际上可以组合成几乎任何东西,甚至是一个返回 split
的简单 std::vector<std::string>
函数:
auto split(const std::string& str, char delimiter) -> std::vector<std::string>
{
const auto range = str |
std::ranges::views::split(delimiter) |
std::ranges::views::transform(to_string);
return {std::ranges::begin(range), std::ranges::end(range)};
}
答案 7 :(得分:0)
您是否有理由不想将string
转换为字符数组(char*
)?调用.c_str()
相当容易。您还可以使用循环和.find()
函数。
答案 8 :(得分:0)
erase()
函数呢?如果您知道要在字符串中拆分的位置,则可以使用erase()
“提取”字符串中的字段。
std::string date("01/02/2019");
std::string day(date);
std::string month(date);
std::string year(date);
day.erase(2, string::npos); // "01"
month.erase(0, 3).erase(2); // "02"
year.erase(0,6); // "2019"
答案 9 :(得分:0)
对于那些没有(想要、需要)C++20
的人来说,这个 C++11
解决方案可能是一种选择。
它在输出迭代器上进行了模板化,因此您可以提供自己的目标位置,将拆分的项目附加到其中,并提供如何处理多个连续分隔字符的选择。
是的,它使用 std::regex
但好吧,如果您已经在 C++11 的快乐土地上,为什么不使用它。
////////////////////////////////////////////////////////////////////////////
//
// Split string "s" into substrings delimited by the character "sep"
// skip_empty indicates what to do with multiple consecutive separation
// characters:
//
// Given s="aap,,noot,,,mies"
// sep=','
//
// then output gets the following written into it:
// skip_empty=true => "aap" "noot" "mies"
// skip_empty=false => "aap" "" "noot" "" "" "mies"
//
////////////////////////////////////////////////////////////////////////////
template <typename OutputIterator>
void string_split(std::string const& s, char sep, OutputIterator output, bool skip_empty=true) {
std::regex rxSplit( std::string("\\")+sep+(skip_empty ? "+" : "") );
std::copy(std::sregex_token_iterator(std::begin(s), std::end(s), rxSplit, -1),
std::sregex_token_iterator(), output);
}