it-swarm.com.de

Analysieren eines durch Kommas getrennten std :: string

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".

110
Piku
#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;
}
131
user229321

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

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;
}
61
Jerry Coffin

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

43
Matthieu N.

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());
17
TC.

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);
  }
}
6
kiamlaluno
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:

  • Keine Abhängigkeiten (z. B. Erhöhung)
  • Kein verrückter Einzeiler
  • Leicht zu verstehen (hoffe ich)
  • Geht perfekt mit Räumen um
  • Ordnet keine Aufteilungen zu, wenn Sie nicht möchten, z. Sie können sie mit einem Lambda wie gezeigt verarbeiten.
  • Fügt keine Zeichen einzeln hinzu - sollte schnell sein.
  • Wenn Sie C++ 17 verwenden, können Sie es ändern, um einen std::stringview zu verwenden. Dann werden keine Zuordnungen vorgenommen und sollten extrem schnell sein.

Einige Design-Optionen, die Sie ändern möchten:

  • Leere Einträge werden nicht ignoriert.
  • Eine leere Zeichenfolge ruft einmal f() auf.

Beispiele für Ein- und Ausgänge:

""      ->   {""}
","     ->   {"", ""}
"1,"    ->   {"1", ""}
"1"     ->   {"1"}
" "     ->   {" "}
"1, 2," ->   {"1", " 2", ""}
" ,, "  ->   {" ", "", " "}
4
Timmmm
#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.

2
Steve Jessop

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.

2
Sheljohn

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];
    }
};
1
mementum
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];
}
1
knapcio

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++;
}
0
Daniel Eckert
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;
  }
0
KeithB

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.

0
tony gil

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