用字符分割字符串

我知道这是一个相当容易的问题,但我只是想为自己解决这个问题一劳永逸

我只想使用一个字符作为拆分分隔符将一个字符串拆分为一个数组。(很像 C # 著名的 。分裂()函数。我当然可以使用蛮力的方法,但我想知道是否还有比这更好的方法。

到目前为止,我已经搜索和可能的 最近的解决方案的方法是使用 (),但是由于它的不便(将您的字符串转换为字符数组等) ,我不喜欢使用它。有没有更简单的方法来实现这一点?

注意: 我想强调这一点,因为人们可能会问“为什么暴力不起作用”。我的强力解决方案是创建一个循环,并在其中使用 Subr ()函数。但是因为它需要 起点和长度,所以当我想分割日期时它就失败了。因为用户可能会输入它为7/12/2012或07/3/2011,在计算下一个“/”分隔符位置之前,我可以真正知道它的长度。

155954 次浏览

看看 Tokenizer

如果您想要卷起您自己的方法,您可以使用 std::string::find()来确定分割点。

使用向量,字符串和字符串流。有点麻烦,但它做的诀窍。

#include <string>
#include <vector>
#include <sstream>


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" };

您不想将 string转换为字符数组(char*)有什么原因吗?调用 .c_str()相当容易。还可以使用循环和 .find()函数。

字符串类
Find ()
C _ str ()

Boost 拥有你在 algorithm/string.hpp中寻找的 Split ():

std::string sample = "07/3/2011";
std::vector<std::string> strs;
boost::split(strs, sample, boost::is_any_of("/"));

另一种方式(C + + 11/升级)为喜欢 RegEx 的人。就个人而言,我非常喜欢正则表达式的这类数据。我的意思是,它比简单地使用分隔符分割字符串要强大得多,因为如果你愿意,你可以选择更聪明地判断什么构成了“有效”数据。

#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));
}

另一种可能性是向流注入使用特殊 ctype方面的区域设置。流使用 ctype facet 来确定什么是“空白”,它将其视为分隔符。使用将分隔符分类为空格的 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>());

很明显,如果你只在一个地方使用它,这会导致过度杀伤。但是,如果您经常使用它,它可以大大有助于保持代码的其余部分非常干净。

我天生不喜欢 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 == std::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!

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"

因为还没有人发布这个: 使用 ranges解决方案非常简单。您可以使用 std::ranges::views::split来分解输入,然后将输入转换为 std::stringstd::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
}

这种方法实际上可以组合成几乎任何东西,甚至是返回 std::vector<std::string>的简单 split函数:

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)};
}

Live Example

对于那些没有(想要,需要) 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);
}

我知道这个解决方案不合理,但它是有效的。这里提供这种方法是为了作为当前问题解决方案的一种变体。

#include <iostream>
#include <vector>
#include <string>
using namespace std;
const int maximumSize=40;
vector<int> visited(maximumSize, 0);
string word;
void showContentVectorString(vector<string>& input)
{
for(int i=0; i<input.size(); ++i)
{
cout<<input[i]<<", ";
}
return;
}
void dfs(int current, int previous, string& input, vector<string>& output, char symbol)
{
if(visited[current]==1)
{
return;
}
visited[current]=1;
string stringSymbol;
stringSymbol.push_back(symbol);
if(input[current]!=stringSymbol[0])
{
word.push_back(input[current]);
}
else
{
output.push_back(word);
word.clear();
}
if(current==(input.size()-1))
{
output.push_back(word);
word.clear();
}
for(int next=(current+1); next<input.size(); ++next)
{
if(next==previous)
{
continue;
}
dfs(next, current, input, output, symbol);
}
return;
}
void solve()
{
string testString="this_is_a_test_string";
vector<string> vectorOfStrings;
dfs(0, -1, testString, vectorOfStrings, '_');
cout<<"vectorOfStrings <- ";
showContentVectorString(vectorOfStrings);
return;
}
int main()
{
solve();
return 0;
}

结果如下:

vectorOfStrings <- this, is, a, test, string,

我已经使用了相当长一段时间的一个解决方案是可以用于向量和列表的拆分

#include <vector>
#include <string>
#include <list>


template< template<typename,typename> class Container, typename Separator >
Container<std::string,std::allocator<std::string> > split( const std::string& line, Separator sep ) {
std::size_t pos = 0;
std::size_t next = 0;
Container<std::string,std::allocator<std::string> > fields;
while ( next != std::string::npos ) {
next = line.find_first_of( sep, pos );
std::string field = next == std::string::npos ? line.substr(pos) : line.substr(pos,next-pos);
fields.push_back(  field );
pos = next + 1;
}
return fields;
}


int main() {
auto res1 = split<std::vector>( "abc,def", ",:" );
auto res2 = split<std::list>( "abc,def", ',' );
}

这是在 C + + 中拆分字符串的另一种方法,在本例中使用的是 wstring,只使用 find 和 subr 函数。

#include <iostream>
#include <vector>


std::vector<std::wstring> SplitWstring(const std::wstring& text, const std::wstring& subText)
{
std::vector<std::wstring> result;
size_t left = 0;
size_t right = text.find(subText);
size_t textSize = text.size();
size_t subTextSize = subText.size();


while (right != std::wstring::npos)
{
if (right > left)
{
size_t size = right - left;
result.push_back(text.substr(left, size));
left = right + subTextSize;
}
else
left += subTextSize;


right = text.find(subText, left);
}


if (left < textSize)
result.push_back(text.substr(left));


return result;
}


int main()
{
//std::wstring text = L"";                          // Result: {}
//std::wstring text = L"-";                         // Result: {"-"}
//std::wstring text = L"ONE";                       // Result: {"ONE"}
//std::wstring text = L"ONE---TWO---THREE";         // Result: {"ONE", "TWO", "THREE"}
std::wstring text = L"---ONE---TWO---THREE---";     // Result: {"ONE", "TWO", "THREE"}
std::wstring subText = L"---";
std::vector<std::wstring> splitted = SplitWstring(text, subText);


if (splitted.size() > 0)
return 1;


return 0;
}