Int aus String in Liste kopieren



  • Nabend Community,
    ich bin noch Anfänger und probier mich gerade an einem Konsolenrechner der folgendermaßen aussehen soll:

    Eingabe: 23+5+7
    Zahl: 23
    Zahl: 5
    Zahl: 7
    Loesung: 35

    Ich hab jetzt die Eingabevariable als string deklariert, die Plus Position abgefragt und in eine Liste geschoben und dann kommt das Problem, wie kann ich aus dem String die Zahlen herauslesen?
    Ich hab mir das so ungefähr vorgestellt:

    eingabe.copy(zahl, laenge, pos);
    lZahlen.push_back(zahl);
    

    Nur muss bei dem copy Befehl ein Char Array als Ziel gewählt werden, aber ich kann ya keine Liste als Char Array definieren?!?

    Ich sitz grad aufm Schlauch, es hat einmal geklappt wenigstens eine Zahl auszulesen, aber danach is dann das Programm abgestürzt <.<.
    Ich komm einfach nicht darauf wie ich den String vernünftig auslese 😕
    Und sollte ich statt ner Liste lieber Vektor nehmen?

    Freue mich auf jede Hilfe und Denkanstoß! ^^



  • Ich verstehe dich nicht für einen Konsolenrechner warum benutzt du da nicht Integer Variablen ??



  • Na, die einfachste Möglichkeit ist die folgende:

    std::vector<unsigned int> vec(1);
    while(std::cin >> vec.back() && std::cin.ignore(1))
        vec.push_back(0);
    

    Also zuerst eine Zahl und dann ein Zeichen extrahieren, wenn beides extrahiert werden konnte dann weitermachen, sonst aufhören.

    Das ist aber sehr primitiv; wenn du das richtig durchziehen möchtest, dann bastle dir einen vernünftigen Lexer.



  • Fragensteller555 schrieb:

    Ich verstehe dich nicht für einen Konsolenrechner warum benutzt du da nicht Integer Variablen ??

    Weil "123+456+78+9" kein Integer ist. Ich will nicht alle Zahlen einzeln einlesen sondern den ganzen Term.

    Sone schrieb:

    Das ist aber sehr primitiv; wenn du das richtig durchziehen möchtest, dann bastle dir einen vernünftigen Lexer.

    Leider sagt mir Lexer nichts ^^

    Und müsste man bei

    std::vector<unsigned int> vec(1);
    while(std::cin >> vec.back() && std::cin.ignore(1))
        vec.push_back(0);
    

    nicht mehrmals Bestätigen, also Enter drücken? (will ich nämlich vermeiden)



  • No0n3 schrieb:

    Und müsste man bei

    std::vector<unsigned int> vec(1);
    while(std::cin >> vec.back() && std::cin.ignore(1))
        vec.push_back(0);
    

    nicht mehrmals Bestätigen, also Enter drücken? (will ich nämlich vermeiden)

    Oh, ja! Aber eigentlich selbe Geschichte, nur müsstest du dann einen String einlesen und dann statt std::cin einen Stringstream benutzen.



  • Hab grad die Referenz zu Stringstream angeschaut und es klingt nach nem guten Stichwort, ich versuch das dann morgen.



  • Du möchtest es vielleicht ungefähr so:

    #include <iostream>
    #include <vector>
    #include <sstream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
    	vector<int> werte;
    	vector<char> operatoren;
    
    	string input;
    	getline(cin, input);     //Eingabe lesen,
    
    	stringstream ss(input);  //in einen Stream bugsieren
    
    	char op;
    	int wert;
    
    	ss >> wert;              //Erste Zahl extrahieren und
    	werte.push_back(wert);   //in den Vektor werte schieben
    
    	while(ss >> op && ss >> wert)  //solange wie möglich Operator und Zahl extrahieren
    	{
    		operatoren.push_back(op);   //und in die entsprechenden Vektoren schieben
    		werte.push_back(wert);
    	}
    
    	//Testausgaben
    	cout << "Werte:\n";
    	for(size_t i = 0; i < werte.size(); ++i)
    		cout << werte[i] << '\n';
    
    	cout << "Operatoren:\n";
    	for(size_t i = 0; i < operatoren.size(); ++i)
    		cout << operatoren[i] << '\n';
    }
    


  • ss >> wert;              //Erste Zahl extrahieren und
    
        while(ss >> op && ss >> wert)  //solange wie möglich Operator und Zahl extrahieren
        {
    

    Woher weiß der Compiler, was er extrahieren soll? Is das einfach so fest geschrieben? (Also je nach dem wie man die Variable deklariert?)
    Und ist der >> Operator nicht auch zum Bits verschieben gemacht? Verwirrt mich gerade 😕
    Ich kenn den >> Operator eig. nur im Zusammenhang mit cin und cout und da hab ich mir vorgestellt er "verschiebt" den Stream dahin ^^

    Edit: Aso, das wird nur abwechselnd extrahiert, stimmts? ^^



  • Nein, der operator>> lässt sich (wie viele andere Operatoren) überladen. Das wurde auch für die Stream-Klasse istream gemacht, so dass er einfach etwas extrahiert.



  • Guckst Du hier



  • Werner_logoff schrieb:

    Guckst Du hier

    Hmm, interessant. Ich hätte das anders gelöst, und zwar mit einem Lexer und einem Parser, der höher bindende Operatoren zuerst auswertet und das Ergebnis im Token-Array ersetzt.

    Leider weiß ich überhaupt nicht wie sich so ein Parser nennt 😕 😃



  • Sone schrieb:

    Ich hätte das anders gelöst, und zwar mit einem Lexer und einem Parser, der höher bindende Operatoren zuerst auswertet und das Ergebnis im Token-Array ersetzt.

    Leider weiß ich überhaupt nicht wie sich so ein Parser nennt 😕 😃

    Wie das Ding'n heißt ist egal. Aber zeige doch mal, wie das funktioniert, aber bitte den Lexer mit std::istream als Input-Device.

    Gruß
    Werner



  • Also Werner!
    Ein Auszug aus einem alten Projekt von mir, stell also keinerlei Ansprüche!
    Ich weiß dass das alles anders vereinfacht werden könnte. Darum geht es jetzt auch nicht.

    boost::ptr_vector<Object> TermLexer::Parser::tokenizeTerm(std::istream& is)
    {
          boost::ptr_vector<Object> rval;
          size_t comma(0);
          bool parsingInt(false);
          Real::real_t current(0);
    
          char c;
          while(is.get(c) and c != '\n')
          {
                if(std::string(Operator::operatorChar).find(c) != std::string::npos)
                {
                      if(parsingInt)
                            rval.push_back(new Real(current));
                      rval.push_back(new Operator(fromChar(c)));
                      comma = current = parsingInt = 0;
                }
                else if(std::isdigit(c) || c == '.')
                {
                      if(c == '.')
                            ++comma;
                      else if(!parsingInt)
                            current = c - '0';
                      else if(!comma)
                            current = current * 10 + c - '0';
                      else
                            current += (c - '0') / (Real::real_t)std::pow(10, comma++);
    
                      parsingInt = true;
                }
                else if(c != ' ')///Skip When Space
                      throw Exceptions::parseException("Invalid Character in Term!");
          }
    
          if(parsingInt)
                rval.push_back(new Real(current));
    
          return rval;
    }
    
    Real::real_t TermLexer::Parser::solveTerm(boost::ptr_vector<Object> objects)
    {
          #define Opiter(x) dynamic_cast<Operator*>(&obj_iter[x])
          #define Realiter(x) dynamic_cast<Real*>(&obj_iter[x])
    
          for(auto obj_iter(objects.begin());obj_iter != objects.end();++obj_iter)
                if(obj_iter->is(Object::type::Operator)
                && Opiter(0)->mOperatorType == Operator::operator_type::openBrace)
                {
                      auto closeBraceiter = obj_iter + 1;
                      for(size_t ct(1);ct;++closeBraceiter)
                      {
                            if(closeBraceiter == objects.end())
                                  throw parseException("An open brace wasn't closed!");
                            else if(closeBraceiter->is(Object::type::Operator))
                                  switch(dynamic_cast<Operator*>(&*closeBraceiter)->mOperatorType)
                                  {
                                        case Operator::operator_type::closeBrace:
                                              --ct;
                                        break;
    
                                        case Operator::operator_type::openBrace:
                                              ++ct;
                                        break;
    
                                        default: break;
                                  }
                      }
    
                      boost::ptr_vector<Object> braceEnclosedObjects(obj_iter + 1, closeBraceiter - 1);
                      Real::real_t res(solveTerm(braceEnclosedObjects));
                      auto before = obj_iter - 1;
                      objects.erase(obj_iter, closeBraceiter);
                      objects.insert(before + 1, new Real(res));
                      obj_iter = before;
                }
    
          auto computeUnaryOperators = [&](std::map<Operator::operator_type, std::function<void(Real&)>> processes)
          {
                for(auto obj_iter(objects.end());obj_iter != objects.begin();)
                {
                      --obj_iter;
                      for(auto pair : processes)
                            if(obj_iter[0].is(Object::type::Operator)
                            && Opiter(0)->mOperatorType == pair.first)
                            {
                                  if(obj_iter + 1 == objects.end()  ///If the operator is before the end of the vector, or the next element is another operator
                                  || !obj_iter[1].is(Object::type::Real))
                                        throw parseException("An unary Operator must occur before a real number!");
                                  else if(obj_iter == objects.begin()///If the object before isn't a Real, compute
                                       || obj_iter[-1].is(Object::type::Operator))
                                  {
                                        pair.second(*Realiter(1));
                                        objects.erase(obj_iter);
                                  }
                            }
                }
          };
    
          auto computeOperator = [&](Operator::operator_type op_Type1, std::function<void(Real&, Real const&)> process1,
                                     Operator::operator_type op_Type2  = static_cast<Operator::operator_type>(-1), std::function<void(Real&, Real const&)> process2 = [](Real&, Real const&){})
          {
                for(auto obj_iter(objects.begin());obj_iter != objects.end();++obj_iter)
                      if(obj_iter->is(Object::type::Operator))
                      {
                            if(obj_iter + 1 == objects.end()
                            || !obj_iter[1].is(Object::type::Real)
                            || obj_iter == objects.begin()
                            || !obj_iter[-1].is(Object::type::Real))
                                  throw parseException("Invalid use of binary operator!");
    
                            if(Opiter(0)->mOperatorType == op_Type1)
                                  process1(*Realiter(-1), *Realiter(1));
                            else if(Opiter(0)->mOperatorType == op_Type2)
                                  process2(*Realiter(-1), *Realiter(1));
                            else continue;
    
                            auto tmpiter = obj_iter - 1;
                            objects.erase(obj_iter + 1);
                            objects.erase(obj_iter);
                            obj_iter = tmpiter;
                      }
          };
    
          computeUnaryOperators({std::make_pair(Operator::operator_type::minus, [&](Real& r){r.mNumber = -r.mNumber;}),
                                 std::make_pair(Operator::operator_type::plus, [&](Real&){}),
                                 std::make_pair(Operator::operator_type::sine, [&](Real& r){r.mNumber = std::sin(TermLexer::Math::toRadian(r.mNumber));}),
                                 std::make_pair(Operator::operator_type::cosine, [&](Real& r){r.mNumber = std::cos(TermLexer::Math::toRadian(r.mNumber));}),
                                 std::make_pair(Operator::operator_type::tangent, [&](Real& r){r.mNumber = std::tan(TermLexer::Math::toRadian(r.mNumber));}),
                                 std::make_pair(Operator::operator_type::sqrt, [&](Real& r){r.mNumber = std::sqrt(r.mNumber);})});
    
          computeOperator(Operator::power, [&](Real& r, Real const& r2){r.mNumber = std::pow(r.mNumber, r2.mNumber);});
    
          computeOperator(Operator::multiply, [](Real& r, Real const& r2){r.mNumber *= r2.mNumber;},
                          Operator::divide, [](Real& r, Real const& r2)
                          {
                                if(!r2.mNumber)
                                   throw std::logic_error("It is not allowed to divide by zero!");
                                r.mNumber /= r2.mNumber;
                          });
    
          computeOperator(Operator::plus, [](Real& r, Real const& r2){r.mNumber += r2.mNumber;},
                          Operator::minus,[](Real& r, Real const& r2){r.mNumber -= r2.mNumber;});
    
          auto front = dynamic_cast<Real*>(&objects.front());
    
          if(!front)
                throw parseException("Unknown parse error occured!");
    
          return front->mNumber;
    }
    

    Auf eine Erklärung habe ich erst Morgen Früh Lust.
    Nur ganz kurz: Die beiden Token sind Real und Operator (also jeweils für Zahl und arithmetischen Operator), beide Kindklassen von Object, der abstrakten Basisklasse. Die erste Funktion oben ist der Lexer, die zweite der eigentliche semantische Parser. Der Rest dürfte sich vom Kontext erschließen.


Anmelden zum Antworten