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äserIch 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_koordNachdem 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 VorzeichenSoweit 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 ähnlichOder 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 eingegebenEDIT://
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öschenDa 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 GBin 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 ...
-
geSTL'te Version:
void ExtractCoords( string text, map<char,double>& coords ) { string::reverse_iterator pos = text.rend(); while ( ( pos = find_if(text.rbegin(), text.rend(), isAlpha) ) != text.rend() ) { size_t dis = distance(pos,text.rend()); coords[*pos] = lexical_cast<double>( text.substr(dis) ); text.erase(dis-1); } }