String teilen



  • Guten Tag zusammen,

    ich hätte da ein Problem, bei dem ihr mir eventuell behilflichsein könntet.
    Und zwar möchte ich eine Funktion schreiben, die einen String nach einem bestimmten Zeichen spalten soll und die substrings dann in einem Array speichet.
    Meine bissherigen lösungsanzätze waren, dass ich mit mit substr() gearbeitet habe jedoch weiß ich nicht wie ich den Substring aus dem inputstring entferne, damit ich den nächsten Substring erhalte.

    Hier meine Ansatz bis jetzt:

    void main()
    {
    	string input="12;33;34;22;11;823";
    	char delim=';';
    	int output[200]={0};
    
    	terminator(input, delim, output);
    
    }
    
    void terminator(string input, char delim, int output[])
    {
    	string temp;
    	int last=0;
    
    	while(last!=-1)
    	{
    	last = input.find_last_of(";");
    	temp=input.substr(last+1);
    	cout<<temp;
    	getch();
    	}
    
    }
    

    Wäre super wenn ihr mir weiter helfen könntet.

    mfg Mark


  • Mod

    String in Stringstream packen (erschlägt das Problem mit dem Entfernen) und getline (bei getline kann man Trennzeichen angeben) benutzen.



  • und nicht int output[] sondern lieber std::vectorstd::string



  • Version 1 (stringstream)

    void split(string str,vector <string> &vec,char delemiter)
    {
    	stringstream sstr(str);
    	if(vec.size()) vector<string>().swap(vec);
    	while(getline(sstr,str,delemiter)) vec.push_back(str);
    }
    

    Version 2 (ohne stringstream geringfügig schneller)

    void split(const string &source,vector <string> &split,char delemiter)
    {
    	unsigned p(-1),o;
    	if(split.size()) vector<string>().swap(split);
    	do
    	{
    	o=++p;
    	p=source.find(delemiter,p);
    	split.push_back(source.substr(o,p-o));
    	}
    	while(p!=string::npos);
    }
    


  • da ist noch ein kleiner bug bzw. überbleibsel (int und nicht unsigned).

    void split(const string &source,vector <string> &split,char delemiter) 
    { 
        int p(-1),o; 
        if(split.size()) vector<string>().swap(split); 
        do 
        { 
        o=++p; 
        p=source.find(delemiter,p); 
        split.push_back(source.substr(o,p-o)); 
        } 
        while(p!=string::npos); 
    }
    


  • Ist das normal, dass man bei Iteratoren ein Assertion failed um die Ohren bekommt, sobald ein Iterator mal außerhalb seiner üblichen Umgebung ist?
    Das verhindert dann nämlich hier z.B. eine schöne Schleifenbedingung:
    (Ich hoffe mal ich übersehe da etwas..)

    void arnold(std::string::iterator first, std::string::iterator last, 
      char delim, std::vector<std::string>& output)
    {
      std::string::iterator it = std::find(first, last, delim);
      while (it != last)
      {
        output.push_back(std::string(first, it));
        first = it + 1;
        it = std::find(first, last, delim);
      }
      output.push_back(std::string(first, it));
    }
    
    int main()
    {
      std::string input = "12;33;34;22;11;823";
      std::vector<std::string> output;
      char delim = ';';
    
      arnold(input.begin(), input.end(), delim, output);
    
      for (std::vector<std::string>::iterator i = output.begin(); 
        i < output.end(); ++i)
      {
        std::cout << *i << std::endl;
      }
    }
    


  • boost::algorithm 😛



  • Hab ich neulich geschrieben, funktioniert auch mit std::vector und anderen Containern und du kannst noch eine Compare-Funktion angeben, falls du die Objekte nicht mit dem ==-Operator vergleichen willst: (Achtung, C++0x dabei!)

    //works for vectors as well as for strings
    //GetTokenized("a:bc:def::g", ':') == {"a", "bc", "def", "g"}
    template<typename T> bool DefaultCompareFunc(T const &first, T const &second)
    {
        return first == second;
    }
    
    template<typename T, typename... ExtraArgs, template<class T, class... ExtraArgs> class Container, typename FuncType>
        std::vector<Container<T> > GetTokenized(Container<T> const &in, T const &comp_value, FuncType comp_func)
    {
        std::vector<Container<T> > ret;
    
        typename Container<T>::const_iterator start_iter = in.begin(), end_iter = in.begin();
        while(end_iter != in.end())
        {
            if(comp_func(*end_iter, comp_value))
            {
                if(start_iter != end_iter)
                {
                    ret.push_back(Container<T>(start_iter, end_iter));
                }
                start_iter = end_iter;
                ++start_iter;
            }
            ++end_iter;
        }
    
        return ret;
    }
    
    template<typename T, typename... ExtraArgs, template<class T, class... ExtraArgs> class Container>
        std::vector<Container<T> > GetTokenized(Container<T> const &in, T const &comp_value)
    {
        return GetTokenized(in, comp_value, DefaultCompareFunc<T>);
    }
    


  • Wie wäre es mit boost::split?



  • Habe alle Versionen mal in Bezug auf Geschwindigkeit durchgetestet.
    Habe einen string mit 6 Token in einer fünf Millionen! Loop zerlegt.(ich weiss das ist ein sehr realitätsfremdes Beispiel)
    1. vector füllen über string::find und substr. (ca. 31 Sek.)
    2. vector füllen über algorithm::find und string::iterator (ca. 41 Sek.)
    3. vector füllen über stringstream (ca. 51 Sek.)
    4. vector füllen über boost::split (ca. 73 Sek.)
    Überrascht hat mich eigentlich nur das Ergebnis von boost::split.
    Die stringstream Variante wird wohl durch das kopieren des strings in den stream ausgebremst.



  • Danke ich konnte das Problem jetzt lösen.
    Nun bin ich an dem Problem angelangt das ich den vector<string> nicht in Integer convertiert kriege.
    Mit atoi gehts nicht. Hat irgendjemand dazu auch eine Idee?

    Vielen Dank.



  • for(int i = 0; i < bis.size(); i++) 
    {      
    	 output.push_back( atoi( bis.at(i).c_str() ) ); 
    }
    

    Hab es geschafft den String in integer zu kovertieren, danke



  • Wenn du eh Zahlen haben möchtest, mach es doch in einem Rutsch.

    void arnold(const char *s, std::vector<int>& output)
    {
      const char *p = s - 1;
      s += std::strlen(s);
      while (p < s)
        output.push_back(std::strtol(p + 1, const_cast<char**>(&p), 10));
    }
    

    Und wenn du jetzt als "delim" ein einfaches Leerzeichen nimmst, kannst du dir sogar das hässliche p + 1 sparen.



  • @cookie: Was soll denn diese hässliche C-Lösung hier bringen? Wenn schon, dann bitte richtig:

    void arnold(const string& s,vector<int>& out)
    {
      stringstream str(s);
      int val;
      while(str>>val)
      {
        str.get();//zum Verarbeiten des Trennzeichens
        out.push_back(val);
      }
    }
    

    (und bestimmt findet noch jemand eine Lösung mit std::copy() und Stream-Iteratoren :D)



  • Ist das gemogelt?

    vector<int> arnold(string input)
    {
    	vector<int> result;
    	replace(input.begin(), input.end(), ';', ' ');
    	stringstream stream(input);
    	copy(istream_iterator<int>(stream), istream_iterator<int>(), back_inserter(result));
    	return result;
    }
    


  • Für sowas ist das zuviel overhead ...



  • 372368 schrieb:

    Für sowas ist das zuviel overhead ...

    Inwiefern?



  • CStoll schrieb:

    @cookie: Was soll denn diese hässliche C-Lösung hier bringen?

    Nachdem er ja eh schon .c_str() genutzt hat, war mir das const char* jetzt auch recht. Und nachdem ich leider festellen musste, dass die C++ Streams einfach unfassbar langsam sind, nutze ich strtol() sobald man sehr viele Zahlen umwandeln muss. Es wird nicht unübersichtlicher und die Streams bieten mir auch keinen erkennbaren Vorteil.
    (Deine Lösung braucht auf meinem System z.B. ~25 mal so lange wie meine.)



  • cooky451 schrieb:

    (Deine Lösung braucht auf meinem System z.B. ~25 mal so lange wie meine.)

    Das glaub ich dir nicht so recht. Wie sieht dein Testprogramm aus?



  • Michael E. schrieb:

    cooky451 schrieb:

    (Deine Lösung braucht auf meinem System z.B. ~25 mal so lange wie meine.)

    Das glaub ich dir nicht so recht. Wie sieht dein Testprogramm aus?

    Ist doch Standard.
    ifstreams schaffen es bei mir teilweise nichtmal den Plattencontroller auszulasten (Raid 0 mit 2 Platten), langsamer wie ne HDD - das muss man erstmal schaffen.


Log in to reply