Java verfügt über eine praktische Split-Methode:
String str = "The quick brown fox";
String[] results = str.split(" ");
Gibt es eine einfache Möglichkeit, dies in C++ zu tun?
Java verfügt über eine praktische Split-Methode:
String str = "The quick brown fox";
String[] results = str.split(" ");
Gibt es eine einfache Möglichkeit, dies in C++ zu tun?
Es erscheint mir seltsam, dass bei all den geschwindigkeitsbewussten Nerds hier auf SO noch niemand eine Version vorgestellt hat, die eine zur Kompilierzeit generierte Nachschlagetabelle für den Begrenzer verwendet (Beispielimplementierung weiter unten). Mit einer Nachschlagetabelle und Iteratoren sollte std::regex in Effizienz zu schlagen, wenn Sie nicht brauchen, um regex zu schlagen, verwenden Sie es einfach, seine Standard ab C++11 und super flexibel.
Einige haben bereits regex vorgeschlagen, aber für die Neulinge hier ist ein gepacktes Beispiel, das genau das tun sollte, was der OP erwartet:
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};
while (std::regex_search (it,end,m,e)) {
ret.emplace_back(m.str());
std::advance(it, m.position() + m.length()); //next start position = match position + match length
}
return ret;
}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){ //comfort version calls flexible version
return split(s.cbegin(), s.cend(), std::move(e));
}
int main ()
{
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};
auto v = split(str);
for(const auto&s:v){
std::cout << s << std::endl;
}
std::cout << "crazy version:" << std::endl;
v = split(str, std::regex{"[^e]+"}); //using e as delim shows flexibility
for(const auto&s:v){
std::cout << s << std::endl;
}
return 0;
}
Wenn wir schneller sein wollen und die Einschränkung akzeptieren, dass alle Zeichen 8 Bit lang sein müssen, können wir zur Kompilierzeit eine Tabelle mit Metaprogrammierung erstellen:
template<bool...> struct BoolSequence{}; //just here to hold bools
template<char...> struct CharSequence{}; //just here to hold chars
template<typename T, char C> struct Contains; //generic
template<char First, char... Cs, char Match> //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
Contains<CharSequence<Cs...>, Match>{}; //strip first and increase index
template<char First, char... Cs> //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {};
template<char Match> //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};
template<int I, typename T, typename U>
struct MakeSequence; //generic
template<int I, bool... Bs, typename U>
struct MakeSequence<I,BoolSequence<Bs...>, U>: //not last
MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U>
struct MakeSequence<0,BoolSequence<Bs...>,U>{ //last
using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
/* could be made constexpr but not yet supported by MSVC */
static bool isDelim(const char c){
static const bool table[256] = {Bs...};
return table[static_cast<int>(c)];
}
};
using Delims = CharSequence<'.',',',' ',':','\n'>; //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<256,BoolSequence<>,Delims>::Type>;
Damit ist die Herstellung einer getNextToken
Funktion ist einfach:
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end
auto second = std::find_if(begin,end,Table{}); //find first delim or end
return std::make_pair(begin,second);
}
Auch die Benutzung ist einfach:
int main() {
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};
auto it = std::begin(s);
auto end = std::end(s);
while(it != std::end(s)){
auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second) << std::endl;
it = token.second;
}
return 0;
}
Hier ist ein Beispiel aus der Praxis: http://ideone.com/GKtkLQ
Ich habe gerade alle Antworten gelesen und kann keine Lösung mit den nächsten Voraussetzungen finden:
Hier ist also meine Lösung
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string_view>
#include <utility>
struct split_by_spaces
{
std::string_view text;
static constexpr char delim = ' ';
struct iterator
{
const std::string_view& text;
std::size_t cur_pos;
std::size_t end_pos;
std::string_view operator*() const
{
return { &text[cur_pos], end_pos - cur_pos };
}
bool operator==(const iterator& other) const
{
return cur_pos == other.cur_pos && end_pos == other.end_pos;
}
bool operator!=(const iterator& other) const
{
return !(*this == other);
}
iterator& operator++()
{
cur_pos = text.find_first_not_of(delim, end_pos);
if (cur_pos == std::string_view::npos)
{
cur_pos = text.size();
end_pos = cur_pos;
return *this;
}
end_pos = text.find(delim, cur_pos);
if (cur_pos == std::string_view::npos)
{
end_pos = text.size();
}
return *this;
}
};
[[nodiscard]] iterator begin() const
{
auto start = text.find_first_not_of(delim);
if (start == std::string_view::npos)
{
return iterator{ text, text.size(), text.size() };
}
auto end_word = text.find(delim, start);
if (end_word == std::string_view::npos)
{
end_word = text.size();
}
return iterator{ text, start, end_word };
}
[[nodiscard]] iterator end() const
{
return iterator{ text, text.size(), text.size() };
}
};
int main(int argc, char** argv)
{
using namespace std::literals;
auto str = " there should be no memory allocation during parsing"
" into words this line and you should'n create any"
" contaner for intermediate words "sv;
auto comma = "";
for (std::string_view word : split_by_spaces{ str })
{
std::cout << std::exchange(comma, ",") << std::quoted(word);
}
auto only_spaces = " "sv;
for (std::string_view word : split_by_spaces{ only_spaces })
{
std::cout << "you will not see this line in output" << std::endl;
}
}
Ich habe eine vereinfachte Version (und vielleicht ein wenig effizienter) von https://stackoverflow.com/a/50247503/3976739 für meinen eigenen Gebrauch. Ich hoffe, es würde helfen.
void StrTokenizer(string& source, const char* delimiter, vector<string>& Tokens)
{
size_t new_index = 0;
size_t old_index = 0;
while (new_index != std::string::npos)
{
new_index = source.find(delimiter, old_index);
Tokens.emplace_back(source.substr(old_index, new_index-old_index));
if (new_index != std::string::npos)
old_index = ++new_index;
}
}
Hier ist mein Schweizer® Taschenmesser der String-Tokenizer für die Aufteilung von Strings nach Leerzeichen, die Berücksichtigung von Strings mit einfachen und doppelten Anführungszeichen sowie die Entfernung dieser Zeichen aus den Ergebnissen. Ich habe RegexBuddy 4.x verwendet, um Folgendes zu erzeugen Die meisten des Code-Schnipsels, aber ich habe eine benutzerdefinierte Behandlung für das Entfernen von Anführungszeichen und einige andere Dinge hinzugefügt.
#include <string>
#include <locale>
#include <regex>
std::vector<std::wstring> tokenize_string(std::wstring string_to_tokenize) {
std::vector<std::wstring> tokens;
std::wregex re(LR"(("[^"]*"|'[^']*'|[^"' ]+))", std::regex_constants::collate);
std::wsregex_iterator next( string_to_tokenize.begin(),
string_to_tokenize.end(),
re,
std::regex_constants::match_not_null );
std::wsregex_iterator end;
const wchar_t single_quote = L'\'';
const wchar_t double_quote = L'\"';
while ( next != end ) {
std::wsmatch match = *next;
const std::wstring token = match.str( 0 );
next++;
if (token.length() > 2 && (token.front() == double_quote || token.front() == single_quote))
tokens.emplace_back( std::wstring(token.begin()+1, token.begin()+token.length()-1) );
else
tokens.emplace_back(token);
}
return tokens;
}
Können Sie die Vorteile von boost::make_find_iterator nutzen. So ähnlich wie hier:
template<typename CH>
inline vector< basic_string<CH> > tokenize(
const basic_string<CH> &Input,
const basic_string<CH> &Delimiter,
bool remove_empty_token
) {
typedef typename basic_string<CH>::const_iterator string_iterator_t;
typedef boost::find_iterator< string_iterator_t > string_find_iterator_t;
vector< basic_string<CH> > Result;
string_iterator_t it = Input.begin();
string_iterator_t it_end = Input.end();
for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i != string_find_iterator_t();
++i) {
if(remove_empty_token){
if(it != i->begin())
Result.push_back(basic_string<CH>(it,i->begin()));
}
else
Result.push_back(basic_string<CH>(it,i->begin()));
it = i->end();
}
if(it != it_end)
Result.push_back(basic_string<CH>(it,it_end));
return Result;
}
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
234 Stimmen
Ich kann nicht glauben, dass diese Routineaufgabe in C++ so viel Kopfzerbrechen macht
6 Stimmen
Seine nicht Kopfschmerzen in C + + - es gibt verschiedene Möglichkeiten, um es zu erreichen. Programmierer sind weniger bewusst, C + + als c # - seine über Marketing und Investitionen ... siehe dies für verschiedene C + + Optionen, um das gleiche zu erreichen: cplusplus.com/faq/sequenzen/strings/split
11 Stimmen
@hB0 gehen durch viele Fragen Antworten und immer noch nicht entscheiden, bedeutet, ist ein Kopfschmerz. die eine braucht, dass die Bibliothek, die andere ist nur für Leerzeichen, die andere nicht behandeln Leerzeichen.
3 Stimmen
Mögliches Duplikat von Eine Zeichenkette in C++ aufteilen?
8 Stimmen
Warum muss in C++ alles ein Kampf sein?
2 Stimmen
C++ ist eine leistungsfähige Sprache, die keine Einheitslösung für die Zeichenkettenmanipulation bietet. Für diese Anwendung ist manchmal alles, was Sie wollen, das letzte Token oder die ersten paar Token, aber ein "Split" wird alles tokenisieren, ohne bedarfsorientierte Stopps. Unabhängig davon sind String-Operationen in jeder Sprache teuer, und deshalb hat sich der C++-Standard ausdrücklich gegen übermäßig vereinfachende Schnittstellen ausgesprochen, die die wahre Komplexität verbergen.
0 Stimmen
@JohnPhuNguyen:
a "split" will tokenize everything without any need-based stops
. Kein Scherz. Das ist genau das Verhalten, das erwünscht ist.1 Stimmen
Interessanterweise sind alle Antworten auf diese Frage auch Antworten auf
Why are Java and Python so much more popular than C++?
.1 Stimmen
@stackoverflowuser2010 Ich glaube, Sie haben den Punkt nicht verstanden. Ja, das ist genau das Verhalten, das bei einem einheitlichen Ansatz erwünscht ist, was ich auch gesagt habe. Und es ist relevant für die Antwort auf die Frage, warum es in Java einfacher zu machen ist als in C++. Die CPP-Standardbibliothek vermeidet explizit alle Komfortmethoden, die die algorithmische Komplexität abstrahieren - deshalb können One-Size-Fits-All-Methoden angeboten werden, werden aber nicht angeboten.
0 Stimmen
@JohnPhuNguyen: Ich verstehe nicht, was Sie meinen mit
one size fits all
. In dieser Frage gibt es nur eine Größe: Tokenize a string. Es gibt keine andere Größe.0 Stimmen
@stackoverflowuser2010 Was ich meine ist, dass die Tokenisierung eine teure Funktion ist, die oft generisch verwendet wird, auch wenn sie nicht benötigt wird. C++ fördert die Implementierung spezifischer Anwendungsfälle anstelle der Tokenisierung, da der eigentliche Akt der Tokenisierung selten benötigt wird. Es wird oft als Mittel verwendet, um etwas in einer Zeichenkette zu finden. Stattdessen bietet C++ (unter anderem) Regex-Matching, das zwar teuer ist, aber bei weitem nicht so teuer wie die String-Manipulation, die in Tokenizern stattfindet. Ja, ein Tokenizer wird leicht für jede Lösung passen, aber er ist selten die beste Lösung. C++ rät davon ab, diesen Weg zu gehen.
1 Stimmen
Eine Lösung für genau diese Frage scheint hier zu liegen: riptutorial.com/cplusplus/example/2148/tokenize