Zahlenwerte aus einem String "filtern"



  • Hallo, ich bin neu hier (mein 1. Beitrag)

    Mein Name ist Andreas, bin 24 Jahre und
    Arbeite als NC-Programierer / CNC-Fräser

    Ich fange gerade an bzw. ich versuche C++ zu lernen, und ich
    hoffe dass ihr mich dabei etwas unterstützen könnt.

    Ich habe vor mir einen Postprozessor zu schreiben mit dem ich die Fräsprogramme die vom CAD-System kommen
    in ein NC-Steuerungs Kompatibles Format umwandeln kann. Im Klartext schaut das so aus:

    Ich muss eine Datei öffnen und diese dann Zeilenweise auswerten, die zeile schaut ungefähr so aus:

    N142X-342765Y31357Z36970G00

    und diese muss ich in verschiedene Bestandteile Zerlegen

    N142 ist unwichtig

    -342765 soll als zahl ohne X als double in der Variable x_koord abgelegt werden
    31357 als double in der Variable y_koord
    36970 als double in der Variable z_koord

    Nachdem die Zeile zerlegt und entsprechend zugewiesen ist wird diese verändert (koordinaten umgerechnet)
    und dann in eine neue Datei geschrieben. Wenn die Zeile abgeschlossen ist kommt die nächste dran !!

    Ich hab da mal folgendes geschrieben:

    string programmzeile;								// Variablendeklaration
    
    cout << "Geb bitte eine NC-Programmzeile ein: ";	// Textzeile einlesen:
    getline ( cin, programmzeile);
    
    string x_edit = programmzeile;						// Programmzeile in die Variable x_edit kopieren
    int xanfang = x_edit.rfind("x")+1;					// Anfang der X-Koordinate suchen und X-Buchstabe entfernen
    x_edit = x_edit.erase(0,xanfang);					// Zeilenanfang bis X-Koordinate löschen
    int satzlaenge = x_edit.length()-1;					// Länge der restlichen Zeile bestimmen
    int xende = x_edit.rfind("y");						// Anfang der Y-Koordinate suchen
    x_edit = x_edit.erase(xende,satzlaenge);			// von Anfang der Y-Koordinate bis Zeilenende löschen
    double x_koord = atof (x_edit.c_str());				// X-Koordinate von String in Double umwandeln
    x_koord = x_koord / 1000;							// 1000stel in ganze MM umrechnen
    cout.precision(3);									// Genauigkeit auf 3 Stellen setzen
    cout << showpoint << fixed << showpos				// X-Koordinate ausgeben mit 3 Nachkommastellen und
    	 << "\n\nX-Koordinate: " << x_koord << "\n\n";	// positiven Vorzeichen
    

    Soweit sogut, Wenn ich die Zeile "N142X-342765Y31357Z36970G00" eingebe erhalte ich als Rückgabewert: -342.765 !! So soll´s sein !!

    Ich habe jetzt nur ein Problem festzustellen wann die X-Koordinate zuende ist !! Ich hab´s jetzt so gelöst,
    dass am Anfang der Y-Koordinate schluss ist, aber es gibt fälle wo´s kein Y mehr gibt, und dann kommt ein Fehler !!

    Jetzt zur eigentlichen Frage:

    Gibt es eine andere Möglichkeit das ende der X-Koordinate zu ermitteln ??
    z.B. "Lese ab "x" alle Zahlen und beim nächsten Zeichen ist schluss"oder so ähnlich

    Oder gibt´s noch ne ganz andere Möglichkeit die Koordinaten aus der Zeile zu filtern ??

    Ich hoffe dass war jetzt nicht zuviel auf einmal 🙄 😕

    Gruß Andi



  • Du kannst ja das Ergebnis von der Suche nach y prüfen.
    wenn npos zurückgeliefert wird, gibt es kein "y" in deinem string ...

    also

    if(xende != std::string::npos)
      // y Koordinate ausgelesen
    else  
      // keine y Koordinate eingegeben
    

    EDIT://
    Ach ja, cin ist die Konsoleneingabe - um dateien einzulesen solltest du dir fstreams anschauen. Um durch Dateisysteme zu manöverieren ist boost::filesystem zu empfehlen ...
    wenn du noch Anregungen willst für Zahl=>String und String=>Zahl
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-39488.html



  • Hi,

    das dürfte nicht gut gehen:

    x_edit = x_edit.erase(xende,satzlaenge);            // von Anfang der Y-Koordinate bis Zeilenende löschen
    

    Da du dann übers Ende hinausschießt. Lass satzlaenge weg und er löscht automatisch von xende bis zum sringende.

    Wieso bentuzt du eigentlich rfind?

    Ich würde ganz normal mit find arbeiten und mit substr meine Teilbereiche rausholen und diese dann ggf umwandeln. So wie du das momentan machst, musst du diese Prozedur ja auch für die anderen Koordinaten machen und ein y_edit und z_edit einführen.



  • @Checker&Murckser

    Danke erstmal, soweit fuktioniert alles :p

    @KasF

    Hast recht, ich muss das ganze dann noch für Y und Z auch noch hintendran hängen !!
    Eventuell auch noch für die Drehachsen A, B und C und dann noch G

    Bin wie gesagt noch Anfänger, und weis auch noch nicht welche möglichkeiten
    mir da offen stehn !!

    find und substr kenn ich jetzt noch nicht 😕
    Kannst du mal ne ganz einfache Zeile Posten, die ich als Vorlage benutzen kann !!

    Muss gleich mal in meiner C++ Referenz nachschauen !!



  • string text = "N142X-342765Y31357Z36970G00";
    
    size_t x_start = text.find("X"), y_start = text.find("Y"),
           z_start = text.find("Z");
    
    cout << text.substr(x_start + 1, y_start - x_start - 1);
    

    Natürlich die find-Sachen auf npos prüfen.

    http://www.cppreference.com/cppstring/substr.html
    http://www.cppreference.com/cppstring/find.html



  • Oder du bastelst dir eine Methode, die den String flexibel zerlegt.
    Stelle ich mir ungefähr so vor:

    bool ExtractCoords( string text, map<char,double>& coords )
    {
        // Zerlegt hier den String in die einzelnen Teile und schreibt
        //   zu jedem darin vorkommenden Buchstaben den zugehörigen Zahlenwert
        //   in die map.
    }
    
    ...
    
    string text = "N142X-342765Y31357Z36970G00";
    map<char,double> coords;
    if ( ! ExtractCoords( text, coords ) )
    {
        // Fehler
    }
    else
    {
        // coords['X'] beinhaltet die x-Koordinate usw.
    }
    


  • std::string text = "N142X-342765Y31357Z36970G00";
    
    void extract_coords(const std::string& string, double& x, double& y, double& z)
    {
        std::size_t pos_x = string.find_first_of('X');
        std::size_t pos_y = string.find_first_of('Y', pos_x);
        std::size_t pos_z = string.find_first_of('Z', pos_y);
        std::size_t pos_end = string.find_first_not_of("0123456789", pos_z);
        if (pos_x == std::string::npos || pos_y == std::string::npos || pos_z == std::string::npos)
            throw std::logical_error("Invalid string!");
    
        std::stringstream ss(string.substr(pos_x + 1, pos_y));
        ss >> x;
        ss.clear();
        ss.str("");
        ss << string.substr(pos_y + 1, pos_z);
        ss >> y;
        ss.clear();
        ss.str("");
        ss << string.substr(pos_z + 1, pos_end);
        ss >> z;
    }
    

    ist aber nicht die kürzeste Lösung 😞



  • Ich bin nicht besonders gut mit der STL, aber das hier geht auch:

    void ExtractCoords( string text, map<char,double>& coords )
    {
    	char last_char = '\0';
    	for ( size_t i=0, last=-1, len=text.length(); i<len; ++i )
    	{
    		if ( isalpha( text[i] ) )
    		{
    			char c = text[i];
    			if ( last != -1 )
    			{
    				text[i] = '\0';
    				coords[last_char] = atof( text.c_str()+last );
    			}
    			last_char = c;
    			last = i+1;
    		}
    	}
    	if ( last != -1 )
    		coords[last_char] = atof( text.c_str()+last );
    }
    

    edit: Nur aus Interesse, wie kann man das elegant mit der STL machen 😕



  • ok, funktioniert jetzt einigermasen, jetzt versuche ich
    das alles in eine Funktion zu packen um diese dann in eine
    externe Headerdatei auszulagern, aber irgendwie will´s nich so
    recht funktionieren.

    Kann ich der Funktion ein String übergeben und als
    Rückgabewert eine double erhalten ??

    Ich bekomme als Rückgabewerd x_koord immer "999999999999999"
    so als wenn der String überhaupt nicht bearbeitet wird !!

    #include <cstdlib>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    double x_filter(string);
    
    int main(int argc, _TCHAR* argv[])
    {
    	string programmzeile;								// Variablendeklaration
    	double x_koord;
    
    	cout << "NC-Programmzeile eingeben: ";				// Textzeile einlesen:
    	getline ( cin, programmzeile);
    
    	x_koord = x_filter(programmzeile);
    
    	cout.precision(3);									// Genauigkeit auf 3 Stellen setzen
    	cout << showpoint << fixed << showpos				// X-Koordinate ausgeben mit 3 Nachkommastellen und
    		 << "\n\nX-Koordinate: " << x_koord << "\n\n";	// positiven Vorzeichen
    
    	system("PAUSE");
    	return 0;
    }
    
    double x_filter(string)
    {
    	string x_edit;
    	double x_koord;									// Programmzeile in die Variable x_edit kopieren
    	int xanfang = x_edit.rfind("x");				// Anfang der X-Koordinate suchen und X-Buchstabe entfernen
    	if (xanfang != std::string::npos)				// Wenn kein X vorhanden ist an Verzweigungsende Springen
    	{	xanfang++;
    		x_edit = x_edit.erase(0,xanfang);				// Zeilenanfang bis X-Koordinate löschen
    		int satzlaenge = x_edit.length()-1;				// Länge der restlichen Zeile bestimmen
    		int xende = x_edit.rfind("y");					// Anfang der Y-Koordinate suchen
    		if (xende != std::string::npos);				// Wenn kein Y vorhanden ist nach Z-Koordinate suchen
    			else xende = x_edit.rfind("z");
    		if (xende != std::string::npos);				// Wenn auch kein Z vorhanden ist nach G Suchen
    			else xende = x_edit.rfind("g");
    			if (xende != std::string::npos);			// Wenn auch kein G vorhanden ist dann Zeilenende nehmen
    			else xende = satzlaenge+1;
    		x_edit = x_edit.erase(xende,satzlaenge);		// von Ende der X-Koordinate bis Zeilenende löschen
    		x_koord = atof (x_edit.c_str());				// X-Koordinate von String in Double umwandeln
    		x_koord = x_koord / 1000;}						// 1000stel in ganze MM umrechnen
    	else x_koord = 999999999999999;					// Wenn kein X vorhanden ist setz den wert auf 999999999999999
    	return x_koord;
    }
    

    Was hab ich da falsch gemacht ??

    Es geht mir jetzt nur um den richtigen Rückgabwert, die
    Zerlegung des String´s werde ich dann noch umschreiben, entsprechend
    euren vorschlägen !!



  • 'x' != 'X' 😉

    if(x_edit.find('x') != string::npos || x_edit.find('X') != string::npos)
    {
    ...
    }
    else ... //9999999
    


  • Badestrand schrieb:

    edit: Nur aus Interesse, wie kann man das elegant mit der STL machen 😕

    Vielleicht so:

    void ExtractCoords( string text, map<char,double>& coords )
    {
    
    	char last_char = 0;
    	string::iterator pos = text.end(), last_pos = text.end();
    
    	while ( (pos = find_if(text.begin(),text.end(),isalpha)) != text.end() )
    	{ 
    		if( pos == text.begin() ) 
    		{ 
    			last_char = *pos;
    			last_pos = pos; 
    			*pos = 0; 
    
    			continue; 
    		}
    
    		stringstream sstr("");
    		copy(last_pos+1,pos,ostream_iterator<char>(sstr)); //da es blöderweise kein string::substr für iteratorn gibt
    
    		double val = 0;
    		sstr >> val;
    
    		coords[last_char] = val;
    
    		last_char = *pos;
    		last_pos = pos;
    		*pos = 0;
    	}
    
    }
    

    Edit: gcc aus C::B will die find_if Anweisung nicht kompilieren 😡



  • KasF schrieb:

    copy(last_pos+1,pos,ostream_iterator<char>(sstr)); //da es blöderweise kein string::substr für iteratorn gibt
    

    😕 Meinst Du folgendes?

    string newstring(last_pos + 1, pos);
    


  • KasF schrieb:

    Edit: gcc aus C::B will die find_if Anweisung nicht kompilieren 😡

    Das liegt daran dass isalpha dort wohl ein Makro ist (und imho auch sein darf). Zusätzlich könnten Dir die Überladungen aus <locale> einen Strich durch die Rechnung machen (betrifft alle Compiler).



  • LordJaxom schrieb:

    KasF schrieb:

    Edit: gcc aus C::B will die find_if Anweisung nicht kompilieren 😡

    Das liegt daran dass isalpha dort wohl ein Makro ist (und imho auch sein darf).

    AFAIK ist 'isalpha' sowieso nur mit 'int'-Parametern definiert, d.h. die Signatur passt hier eh nicht.



  • Konrad Rudolph schrieb:

    😕 Meinst Du folgendes?

    string newstring(last_pos + 1, pos);
    

    🙄 🙄
    Wollte eigentlich sstr nen substring übergeben, aber habe voll vergessen, dass man einen string auch so konstruieren kann.

    LordJaxom schrieb:

    Das liegt daran dass isalpha dort wohl ein Makro ist (und imho auch sein darf). Zusätzlich könnten Dir die Überladungen aus <locale> einen Strich durch die Rechnung machen (betrifft alle Compiler).

    Achso, danke.

    bool isAlpha(char c)
    {
        return isalpha(c);
    }
    ...
    find_if(text.begin(),text.end(),isAlpha)
    ...
    stringstream sstr( string(last_pos + 1,pos) );
    


  • Du musst aber, glaube ich, noch ne Kleinigkeit ändern, 'G' wird noch nicht erfasst (bei mir jedenfalls nicht). Das Problem hatte ich auch erst :p

    ^Und ich hab meinen Code nochmal verändert, 'text' kann ich ja verändern wie ich will :)^



  • Badestrand schrieb:

    Du musst aber, glaube ich, noch ne Kleinigkeit ändern, 'G' wird noch nicht erfasst (bei mir jedenfalls nicht). Das Problem hatte ich auch erst :p

    bool isAlpha(char c)
    {
        return isalpha(c);
    }
    
    void ExtractCoords( string text, map<char,double>& coords )
    {
    
        char last_char = 0;
        string::iterator pos = text.end(), last_pos = text.end();
    
        while ( (pos = find_if(text.begin(),text.end(),isAlpha)) != text.end() )
        {
            if( pos == text.begin() )
            {
                last_char = *pos;
                last_pos = pos;
                *pos = 0;
    
                continue;
            }
    
            stringstream sstr( string(last_pos + 1,pos) );
    
            double val = 0;
            sstr >> val;
    
            coords[last_char] = val;
    
            last_char = *pos;
            last_pos = pos;
            *pos = 0;
        }
    
         stringstream sstr( string(last_pos+1,text.end()) );
    
         double val = 0;
         sstr >> val;
    
         coords[last_char] = val;
    
    }
    

    🙂
    Das kann man aber bestimmt noch eleganter lösen, vielleicht zaubert ja Konrad ne rekursive Lösung ...



  • Und nochmal was gefrickelt:

    bool isAlpha(char c)
    {
        return isalpha(c);
    }
    
    template<class T, class S>
    T lexical_cast(const S& val)
    {
        stringstream sstr("");
        T newval;
        if( !( sstr << val && sstr >> newval ) )
           throw bad_cast();
    
        return newval;
    }
    
    void ExtractCoords( string text, map<char,double>& coords )
    {
    
        typedef string::iterator sIT;
    
        struct helpFunc {
            void operator()(char& last_char, sIT& pos, sIT& last_pos)
            {
                last_char = *pos;
                last_pos = pos;
                *pos = 0;
            }
        };
    
        char last_char = 0;
        sIT pos = text.end(), last_pos = text.end();
    
        while ( (pos = find_if(text.begin(),text.end(),isAlpha)) != text.end() )
        {
            if( pos == text.begin() )
            {
                helpFunc()(last_char,pos,last_pos);
                continue;
            }
    
            coords[last_char] = lexical_cast<double>( string(last_pos + 1,pos) );
    
            helpFunc()(last_char,pos,last_pos);
        }
    
         coords[last_char] = coords[last_char] = lexical_cast<double>( string(last_pos+1,text.end()) );
    
    }
    


  • hehe :p:

    void ExtractCoords( string text, map<char,double>& coords )
    {
    	for ( int i=text.length()-1; i>=0; --i )
    	{
    		if ( isalpha( text[i] ) )
    		{
    			coords[ text[i] ] = atof( text.c_str()+i+1 );
    			text[i] = '\0';
    		}
    	}
    }
    


  • Noch ein Nachtschwärmer 🙂

    Elegante Idee von Rückwärts durchzulaufen. Wenn die Sonne aufgeht, schreibe ich meins um ...


Anmelden zum Antworten