Wenn ich einen std :: string habe, der eine durch Kommas getrennte Liste von Zahlen enthält, was ist der einfachste Weg, um die Zahlen zu analysieren und sie in ein Integer-Array zu setzen?
Ich möchte das nicht verallgemeinern, um etwas anderes zu analysieren. Nur eine einfache durch Kommas getrennte Ganzzahl wie "1,1,1,1,2,1,1,1,0".
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
int main()
{
std::string str = "1,2,3,4,5,6";
std::vector<int> vect;
std::stringstream ss(str);
int i;
while (ss >> i)
{
vect.Push_back(i);
if (ss.peek() == ',')
ss.ignore();
}
for (i=0; i< vect.size(); i++)
std::cout << vect.at(i)<<std::endl;
}
Etwas weniger wortreich, std und nimmt alles durch ein Komma getrennt.
stringstream ss( "1,1,1,1, or something else ,1,1,1,0" );
vector<string> result;
while( ss.good() )
{
string substr;
getline( ss, substr, ',' );
result.Push_back( substr );
}
Noch ein anderer Ansatz: Verwenden Sie ein spezielles Gebietsschema, das Kommas als Leerzeichen behandelt:
#include <locale>
#include <vector>
struct csv_reader: std::ctype<char> {
csv_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());
rc[','] = std::ctype_base::space;
rc['\n'] = std::ctype_base::space;
rc[' '] = std::ctype_base::space;
return &rc[0];
}
};
Um dies zu verwenden, imbue()
erstellen Sie einen Stream mit einem Gebietsschema, das diese Facette enthält. Sobald Sie das getan haben, können Sie Zahlen lesen, als ob die Kommas überhaupt nicht da wären. Nur zum Beispiel werden wir kommagetrennte Zahlen von der Eingabe lesen und dann eine pro Zeile in die Standardausgabe schreiben:
#include <algorithm>
#include <iterator>
#include <iostream>
int main() {
std::cin.imbue(std::locale(std::locale(), new csv_reader()));
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
Die C++ String Toolkit Library (Strtk) hat die folgende Lösung für Ihr Problem:
#include <string>
#include <deque>
#include <vector>
#include "strtk.hpp"
int main()
{
std::string int_string = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
std::vector<int> int_list;
strtk::parse(int_string,",",int_list);
std::string double_string = "123.456|789.012|345.678|901.234|567.890";
std::deque<double> double_list;
strtk::parse(double_string,"|",double_list);
return 0;
}
Weitere Beispiele finden Sie hier Here
Alternative Lösung mit generischen Algorithmen und Boost.Tokenizer :
struct ToInt
{
int operator()(string const &str) { return atoi(str.c_str()); }
};
string values = "1,2,3,4,5,9,8,7,6";
vector<int> ints;
tokenizer<> tok(values);
transform(tok.begin(), tok.end(), back_inserter(ints), ToInt());
Sie können auch die folgende Funktion verwenden.
void tokenize(const string& str, vector<string>& tokens, const string& delimiters = ",")
{
// Skip delimiters at beginning.
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first non-delimiter.
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos) {
// Found a token, add it to the vector.
tokens.Push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters.
lastPos = str.find_first_not_of(delimiters, pos);
// Find next non-delimiter.
pos = str.find_first_of(delimiters, lastPos);
}
}
std::string input="1,1,1,1,2,1,1,1,0";
std::vector<long> output;
for(std::string::size_type p0=0,p1=input.find(',');
p1!=std::string::npos || p0!=std::string::npos;
(p0=(p1==std::string::npos)?p1:++p1),p1=input.find(',',p0) )
output.Push_back( strtol(input.c_str()+p0,NULL,0) );
Es ist natürlich eine gute Idee, in strtol()
nach Konvertierungsfehlern zu suchen. Möglicherweise kann der Code auch von anderen Fehlerprüfungen profitieren.
Viele ziemlich schreckliche Antworten hier, also werde ich meine hinzufügen (inklusive Testprogramm):
#include <string>
#include <iostream>
#include <cstddef>
template<typename StringFunction>
void splitString(const std::string &str, char delimiter, StringFunction f) {
std::size_t from = 0;
for (std::size_t i = 0; i < str.size(); ++i) {
if (str[i] == delimiter) {
f(str, from, i);
from = i + 1;
}
}
if (from <= str.size())
f(str, from, str.size());
}
int main(int argc, char* argv[]) {
if (argc != 2)
return 1;
splitString(argv[1], ',', [](const std::string &s, std::size_t from, std::size_t to) {
std::cout << "`" << s.substr(from, to - from) << "`\n";
});
return 0;
}
Schöne Eigenschaften:
std::stringview
zu verwenden. Dann werden keine Zuordnungen vorgenommen und sollten extrem schnell sein.Einige Design-Optionen, die Sie ändern möchten:
Beispiele für Ein- und Ausgänge:
"" -> {""}
"," -> {"", ""}
"1," -> {"1", ""}
"1" -> {"1"}
" " -> {" "}
"1, 2," -> {"1", " 2", ""}
" ,, " -> {" ", "", " "}
#include <sstream>
#include <vector>
const char *input = "1,1,1,1,2,1,1,1,0";
int main() {
std::stringstream ss(input);
std::vector<int> output;
int i;
while (ss >> i) {
output.Push_back(i);
ss.ignore(1);
}
}
Schlechte Eingaben (zum Beispiel aufeinanderfolgende Trennzeichen) führen zu einem Durcheinander, aber Sie sagten einfach.
Ich bin überrascht, dass noch niemand eine Lösung vorgeschlagen hat, die std::regex
verwendet:
#include <string>
#include <algorithm>
#include <vector>
#include <regex>
void parse_csint( const std::string& str, std::vector<int>& result ) {
typedef std::regex_iterator<std::string::const_iterator> re_iterator;
typedef re_iterator::value_type re_iterated;
std::regex re("(\\d+)");
re_iterator rit( str.begin(), str.end(), re );
re_iterator rend;
std::transform( rit, rend, std::back_inserter(result),
[]( const re_iterated& it ){ return std::stoi(it[1]); } );
}
Diese Funktion fügt alle Ganzzahlen an der Rückseite des Eingabevektors ein. Sie können den regulären Ausdruck anpassen, um negative ganze Zahlen oder Gleitkommazahlen usw. einzubeziehen.
Ich kann noch nicht kommentieren (erste Schritte auf der Website), aber eine generischere Version der von Jerry Coffin abgeleiteten fantastischen Klasse des Typs cype zu seinem Beitrag hinzugefügt.
Danke Jerry für die super Idee.
(Weil es Peer-Review sein muss, hier auch vorübergehend hinzugefügt)
struct SeparatorReader: std::ctype<char>
{
template<typename T>
SeparatorReader(const T &seps): std::ctype<char>(get_table(seps), true) {}
template<typename T>
std::ctype_base::mask const *get_table(const T &seps) {
auto &&rc = new std::ctype_base::mask[std::ctype<char>::table_size]();
for(auto &&sep: seps)
rc[static_cast<unsigned char>(sep)] = std::ctype_base::space;
return &rc[0];
}
};
string exp = "token1 token2 token3";
char delimiter = ' ';
vector<string> str;
string acc = "";
for(int i = 0; i < exp.size(); i++)
{
if(exp[i] == delimiter)
{
str.Push_back(acc);
acc = "";
}
else
acc += exp[i];
}
Einfache Copy/Paste-Funktion, basierend auf dem boost-Tokenizer .
void strToIntArray(std::string string, int* array, int array_len) {
boost::tokenizer<> tok(string);
int i = 0;
for(boost::tokenizer<>::iterator beg=tok.begin(); beg!=tok.end();++beg){
if(i < array_len)
array[i] = atoi(beg->c_str());
i++;
}
bool GetList (const std::string& src, std::vector<int>& res)
{
using boost::lexical_cast;
using boost::bad_lexical_cast;
bool success = true;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sepa(",");
tokenizer tokens(src, sepa);
for (tokenizer::iterator tok_iter = tokens.begin();
tok_iter != tokens.end(); ++tok_iter) {
try {
res.Push_back(lexical_cast<int>(*tok_iter));
}
catch (bad_lexical_cast &) {
success = false;
}
}
return success;
}
einfacher Aufbau, leicht anpassbar, einfache Wartung.
std::string stringIn = "my,csv,,is 10233478,separated,by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
if (stringIn[i] == ",") {
commaSeparated.Push_back("");
commaCounter++;
} else {
commaSeparated.at(commaCounter) += stringIn[i];
}
}
am Ende haben Sie einen Vektor von Strings, bei dem jedes Element im Satz durch Leerzeichen getrennt ist. Leere Zeichenfolgen werden als separate Elemente gespeichert.
Dies ist der einfachste Weg, den ich oft benutzt habe. Es funktioniert für jedes Trennzeichen mit einem Zeichen.
#include<bits/stdc++.h>
using namespace std;
int main() {
string str;
cin >> str;
int temp;
vector<int> result;
char ch;
stringstream ss(str);
do
{
ss>>temp;
result.Push_back(temp);
}while(ss>>ch);
for(int i=0 ; i < result.size() ; i++)
cout<<result[i]<<endl;
return 0;
}