# [C++]Nach String suchen, teilen und speichern



## Seelenquell (19. Juni 2007)

Hallo!
Ich hab heute mal ein bisschen im Forum gesucht, habe aber so viele Threads gefunden, die leider nur so ähnlich sind, wie mein Problem, dass ich doch einen eigenen öffne.
Vorab:meine C++-Kenntnisse habe ich aus einer FH-Vorlesung, die schon ein paar Semester zurückliegt.

Die Aufgabenstellung:

Ich hab eine Textdatei, die folgendermaßen aufgebaut ist:

```
[s1?v]{s1?v SUPPLY V00.50C  8051_SIO V00.16}
[c1?v]{c1?v CYLINDER V00.50D  8051_SIO V00.16}
[v1?v]{v1?v VALVE V00.50I  8051_SIO V00.16}
[v2?v]{v2?v VALVE V00.50O  8051_SIO V00.16}
[p1?v]{p1?v0.0.8.3}
[v2?s]{v2?s01 00 01 00 0 0 20}
[s1?s]{s1?s01 01 00}
[s1?k]{s1?k00}
[c1?s]{c1?s01 01 00 0000}
[p1?s]{p1?s00}
[v1?p]{v1?p13 13 12 13}
```

Die geht dann noch so etwa 60000 Zeilen weiter.

Ich will die Zeilen die mit [c1?s] beginnen finden und dort die vierstellige Hex-Zahl, die am Ende der Zeile vor der Klammer steht, herausnehmen und in einer neuen Datei speichern.

Wie würdet ihr das lösen ?
In Worten würde ich es so machen:
Finde Zeile mit [c1?s] am Anfang.
Gehe An Position 21 in der Zeile und lies die 4 Zeichen dort ein.
Speichere die Zeichen in Datei.
Suche nächste Zeile mit dem gegebenen Anfang.

Leider fehlen mir ein bisschen die Befehle dazu. Interessant hat sich bisher getline() angehört.

Danke schonmal!

Lieber Grüße
Seelenquell


----------



## MCoder (20. Juni 2007)

Schau mal hier:

```
#include <string>
#include <fstream>

int main()
{
    std::ifstream infile("input.txt");
    std::ofstream outfile("output.txt");

    if( infile.is_open() && outfile.is_open() )
    {
        std::string strLine;

        while( std::getline(infile, strLine) )
        {
            if( strLine.substr(0,3).compare("[c1") == 0 && strLine.at(4) == 's' )
            {
                outfile << strLine.substr(20,4) << std::endl;
            }
        }

        infile.close();
        outfile.close();
    }

    return 0;
}
```
Gruß
MCoder


----------



## Seelenquell (20. Juni 2007)

In der Zeile 15

```
if( strLine.substr(0,3).compare("[c1") == 0 && strLine.at(4) == 's' )
```
meckert der Compiler: no matching function for call to 'std::basic_string<....
Ich benutze Dev-C++.

Danke aber auf jeden Fall schonmal !


----------



## deepthroat (20. Juni 2007)

Hi.





Seelenquell hat gesagt.:


> In der Zeile 15
> 
> ```
> if( strLine.substr(0,3).compare("[c1") == 0 && strLine.at(4) == 's' )
> ...


Es wäre auf jeden Fall nützlich die ganze Fehlermeldung zu kennen. So kann man da nur raten.

Ich vermute mal dein Compiler (Dev-C++ verwendet den MinGW Compiler) bzw. die verwendete C++ Standardbibliothek kennt die Methode "at" noch nicht.

Versuch's mal so (oder poste die komplette Fehlermeldung!):

```
if( strLine.substr(0,3).compare("[c1") == 0 && strLine[4] == 's' )
```
Gruß


----------



## Seelenquell (20. Juni 2007)

Okay, Fehler gefunden: ich hab ne Klammer vergessen. 
Das Programm läuft jetzt ab, allerdings wird nichts in die Output-Datei geschrieben.


```
#include <cstdlib>
#include <iostream>
#include <string>
#include <fstream>

using namespace std;
 
int main(int argc, char *argv[])
{    
	std::ifstream infile("Logfile.txt");
	std::ofstream outfile("ergebnis.txt");

     if( infile.is_open() && outfile.is_open() )    
	{        
	std::string strLine;         
	while( std::getline(infile, strLine) )        
	{            
		if( strLine.substr(0,3).compare("[c1") == 0 && strLine.at(4) == 's' )
	            {       outfile << strLine.substr(20,4) << std::endl;            
			}        
	}         
	infile.close();        
	outfile.close();    
        }  

   	system("PAUSE");
	return EXIT_SUCCESS;
}
```

Ich hab wahrscheinlich irgendwas offensichtliches vergessen.


----------



## deepthroat (20. Juni 2007)

Schwer zu sagen. Evtl. kann die Eingabedatei nicht gefunden werden?

Evtl. solltest du diesen Fehler explizit abfangen:
	
	
	



```
std::ifstream infile("Logfile.txt");
  std::ofstream outfile("ergebnis.txt");

  if (!infile.is_open()) {
    cerr << "Eingabedatei konnte nicht geöffnet werden." << endl;
    return EXIT_FAILURE;
  }
  if (!outfile.is_open()) {
    cerr << "Ausgabedatei konnte nicht geöffnet werden." << endl;
    return EXIT_FAILURE:
  }
  std::string strLine;
  while (getline( ...
```
Gruß


----------



## Seelenquell (20. Juni 2007)

Habe ich jetzt gemacht. Tatsächlich kann er die Eingabedatei (und wahrscheinlich auch die Ausgabedatei) nicht öffnen. Habe jetzt mal den kompletten Pfad angegeben.
Sehe ich das richtig, dass ich beim Pfad die Backslashes doppelt setzen muss?
Das klappt jedenfalls auch nicht.


```
std::ifstream infile ("C:\\Dokumente und EInstellungen\\Administrator\\Eigene Dateien\\Programme\\Prog2\\Logfile.txt");
```

Oder muss ich die Freizeichen auch ersetzen ?


----------



## MCoder (20. Juni 2007)

Hallo, es müssen nur die Backslashes doppelt gesetzt werden. Bezieht sich die Aussage "klappt nicht" darauf, dass die Eingabedatei nicht geöffnet werden kann? Überprüfe dann doch noch mal den Pfad; das lange Teil am besten auch nicht eintippen, sondern aus der Adresszeile des Explorers kopieren 
Die Ausgabedatei ist kein Problem. Wird kein Pfad angegeben, wird sie im aktuellen Pfad (wo immer das auch gerade ist) angelegt.

Gruß
MCoder


----------



## deepthroat (20. Juni 2007)

Ja, die Backslashes mußt du entwerten, da sie sonst evtl. eine andere Bedeutung bekommen. Die Leerzeichen mußt du nicht entwerten.

Hast du denn wenn du das Programm ausführst die erforderlichen Rechte die Datei in dem Verzeichnis zu lesen?

Gruß


----------



## Seelenquell (20. Juni 2007)

Ja, mit "klappt nicht" meinte ich, dass er die Datei mit dem angegebenen Pfad nicht öffnen konnte.
Das Programm, sowie die Datei liegen im gleichen Ordner in den Eigenen Dateien. Kann es da irgendwelche "rechtlichen Probleme"  geben ? Schreibgeschützt ist jedenfalls nichts.

P.S.: Die Ausgabedatei wird aber erstellt, wenn das Programm abgelaufen ist. Steht halt nur nichts drin.


----------



## deepthroat (20. Juni 2007)

Seelenquell hat gesagt.:


> Ja, mit "klappt nicht" meinte ich, dass er die Datei mit dem angegebenen Pfad nicht öffnen konnte.
> Das Programm, sowie die Datei liegen im gleichen Ordner in den Eigenen Dateien. Kann es da irgendwelche "rechtlichen Probleme"  geben ? Schreibgeschützt ist jedenfalls nichts.
> 
> P.S.: Die Ausgabedatei wird aber erstellt, wenn das Programm abgelaufen ist. Steht halt nur nichts drin.


Und die Ausgabedatei ist dann auch im Ordner wo das Programm und die Eingabedatei sich befinden?

Schreibgeschützt darf die Eingabedatei ja sein - die wird ja sowieso im Nur-Lesen Modus geöffnet.

Evtl. hat die Datei eine zweite Endung? Sprich die Datei heißt in Wirklichkeit "Logfile.txt.txt"?

Gruß


----------



## Seelenquell (20. Juni 2007)

Ihr habt's echt drauf 
Tatsächlich hieß die Logfile.txt.txt.
Jetzt wird die Datei auch geöffnet, die richtigen Daten ausgelesen und in ergebnis.txt gespeichert.
Allerdings bricht er vorher irgendwo ab. Die letzten Datensätze fehlen. Außerdem schließt sich das Commad-Line Fenster von selbst, ohne auf Eingabe zu warten. Kann es sein, dass die Datenmenge zu groß ist ?


----------



## Seelenquell (20. Juni 2007)

Also, er liest genau 3848 Zeilen, die mit [c1?s] anfangen ein, dann bricht er ab. Ich hab das Suchkriterium eingeschränkt, so dass er die Zahl nur einliest, wenn sie nicht mit "0" anfängt. Da bekomme ich nur die Datensätze zurück, die auch ohne die Enschränkung mit dabei waren, er scheint also etwa nach genau der Zeit abzubrechen.

Ideen ? Danke übrigens schonmal vielmals für die Hilfe soweit !


----------



## MCoder (20. Juni 2007)

Das sieht nach Absturz aus. Möglicherweise sind Zeilen dabei, die nicht dem erwarteten Schema entsprechen. Du solltest daher erstmal die Länge der Zeile überprüfen, bevor auf nicht existente Spaltenpositionen zugegriffen wird:

```
if( strLine.size() > 24 )
{
    if( strLine.substr(0,3).compare("[c1") == 0 && strLine.at(4) == 's' )
    {
        outfile << strLine.substr(20,4) << std::endl;
    }
}
```
Gruß
MCoder


----------



## Seelenquell (20. Juni 2007)

Das wars!
Tausend Dank an alle die geholfen haben!
Dann mach ich mich jetzt mal an die Feinheiten. Wird wahrscheinlich noch die eine oder andere Frage kommen.

Gruß
Seelenquell


----------



## Seelenquell (20. Juni 2007)

Okay, wie versprochen, die nächste Frage 
Die Strings, die ich jetzt in der Ausgabedatei habe, sind Hex-Zahlen.
Ich nehme an den String kann man nicht direkt von Hex zu Dez umwandeln.
Wie bekomme ich jetzt also aus dem Hex-String eine Dezimal-Integer ?

Zweck: Die Ausgabedatei soll in Excel geöffnet werden und aus der Tabelle ein Diagramm gemacht werden. Excel kann ohne Add-Ins aber wohl nicht mit Hex umgehen und die sind hier nicht ohne weiteres vorhanden.

Ich habe mir einige Herangehensweisen hier im Forum angesehen, der Compiler meckert aber immer, dass er einen const char braucht und keinen string.

Danke schonmal !


----------



## deepthroat (20. Juni 2007)

Hi.

Umwandlungen von String in andere Typen kannst du in C++ sehr einfach mit einem Stringstream machen:
	
	
	



```
#include <sstream>

std::string hexstr("aeff"); // als Bsp. für die Eingabe
std::istringstream conv(hexstr);
int d;

if (conv >> std::hex >> d) {
  // d == 44799
}
```
Gruß


----------



## Seelenquell (21. Juni 2007)

Hat auch wieder geklappt, danke!

Jetzt versuche ich gerade eine Pfadeingabe zu schreiben.


```
cout<<"Bitte geben Sie den Pfad zur auszulesenden Datei an:\n";
string dateiPfad;
cin>>dateiPfad;
```

Da std::ifstream infile () mit dem String nichts anfangen kann, wandle ich ihn in einen CString um.


```
const char* Pfad = dateiPfad.c_str();
```

Ist wahrscheinlich unglaublich umständlich, mit der Varibalen Pfad kann std:ifstream infile() aber was anfangen. Leider aber nur, wenn ich den Dateinamen eingebe (Datei liegt im selben Verzeichnis, wie das Programm). Wenn ich den kompletten Pfad zur Datei angebe, kann die Datei nicht geöffnet werden. Dabei habe ich die Backslashes durch \\ ersetzt.
Da hänge ich gerade.

Ziel ist es, in das geöffnete Command Line Fenster die Datei zu ziehen und Enter zu drücken, um den Pfad zu übergeben. Dazu müssten wohl die Backslashes ersetzt werden und die Gänsefüßchen in denen der Pfad der gedropten Datei steht entfernt werden.
Wenn aber der Pfad schon zu Fuß eingegeben nicht funktioniert...


----------



## MCoder (21. Juni 2007)

Hallo,

die Methode "c_str()" liefert einfach nur einen Zeiger auf einen char-Buffer, macht aber kein Wandlung in einen "CString" (das ist eine MFC-Klasse). Überigens kannst du dir die Umwandlung in eine Variable sparen und den Ausdruck gleich in "ifstream" einsetzen; dann sieht's auch nicht mehr so umständlich aus 

```
ifstream infile(dateiPfad.c_str());
```

Die Anwendung von "cin" ist eigentlich falsch. Beschwert sich denn nicht der Compiler?
Sollte so aussehen:

```
string dateiPfad;
getline(cin, dateiPfad);
```

Um die Backslashes braucht du dich nicht zu kümmern, die werden von "cin" schon korrekt abgelegt. Die Anführungszeichen müssen allerdings weg:

```
string::size_type nPos;

while( (nPos = dateiPfad.find("\"")) != string::npos )
{
    dateiPfad.replace(nPos, 1, "");
}
```
Gruß
MCoder


----------



## deepthroat (21. Juni 2007)

Seelenquell hat gesagt.:


> Jetzt versuche ich gerade eine Pfadeingabe zu schreiben.
> 
> 
> ```
> ...


Du solltest lieber mit getline Einlesen, da der Eingabeoperator >> nur wortweise einliest und du so nur das erste Wort von der Zeile bis Leerraumzeichen (Leerzeichen, Tab, Newline) kommen erhälst.


Seelenquell hat gesagt.:


> Da std::ifstream infile () mit dem String nichts anfangen kann, wandle ich ihn in einen CString um.
> 
> 
> ```
> ...


Das ist die übliche Vorgehensweise, da die iostream Klassen leider keine std::strings entgegennehmen.


Seelenquell hat gesagt.:


> Leider aber nur, wenn ich den Dateinamen eingebe (Datei liegt im selben Verzeichnis, wie das Programm). Wenn ich den kompletten Pfad zur Datei angebe, kann die Datei nicht geöffnet werden. Dabei habe ich die Backslashes durch \\ ersetzt.
> Da hänge ich gerade.
> 
> Ziel ist es, in das geöffnete Command Line Fenster die Datei zu ziehen und Enter zu drücken, um den Pfad zu übergeben. Dazu müssten wohl die Backslashes ersetzt werden und die Gänsefüßchen in denen der Pfad der gedropten Datei steht entfernt werden.
> Wenn aber der Pfad schon zu Fuß eingegeben nicht funktioniert...


Du mußt die umgekehrten Schrägstriche nur entwerten, wenn du den Pfad im Programm angibst, da die umgekehrten Schrägstriche eine besondere Bedeutung für den C++ Compiler haben.

Die Eingaben, die du auf der Kommandozeile machst, brauchst du nicht nochmal entwerten.

Allerdings müßtest du die Anführungszeichen entfernen, falls dort welche eingefügt wurden. Das kannst du aber relativ einfach machen, indem du schaust ob das erste und letzte Zeichen ein " ist und dann den entsprechenden Substring nimmst.

Gruß


----------



## deepthroat (21. Juni 2007)

MCoder hat gesagt.:


> Die Anwendung von "cin" ist eigentlich falsch. Beschwert sich denn nicht der Compiler?


Warum sollte das falsch sein? Warum sollte sich der Compiler da beschweren?

Gruß


----------



## MCoder (21. Juni 2007)

deepthroat hat gesagt.:


> Warum sollte das falsch sein? Warum sollte sich der Compiler da beschweren?


Nein ist natürlich nicht falsch, da habe ich was durcheinandergebracht :-(


----------



## Seelenquell (21. Juni 2007)

Ein weiteres Mal vielen Dank für die Hilfe !
Das Programm tut jetzt genau das, was ich wollte.

Es spuckt mir eine Textdatei aus, die Wertpaare enthält. Das Ding kann man in Excel öffnen und ein Diagramm daraus machen.

Kann man diesen zweiten Schritt (.txt-->Diagramm)  noch irgendwie vereinfachen, ohne alles neu in VBA zu schreiben (von dem ich keinen blassen Schimmer habe) ? Wohl nicht, oder ?

Gruß
Seelenquell


----------



## Seelenquell (22. Juni 2007)

Könnte man mit 

```
system("Excel.exe datei.csv");
```
Excel starten und die Datei öffnen lassen ?
Würde zumindest einen Schritt sparen. 
Wird Excel damit auf jedem System gefunden, oder muss man den ganzen Pfad angeben ? Notepad.exe findet er auch ohne Pfad.
Wie kann ich es anstellen, dass er anstatt datei.csv die tatsächlich erzeugt Ausgabedatei öffnet? Da müsste ich doch sowas ähnliches wie 

```
system("Excel.exe szAusgabepfad");
```
machen. Das ist aber leider nicht richtig.

P.S.:
Hab das mit dem system-Befehl jetzt hinbekommen. Allerdings wird mit

```
string excel ="c:\\Programme\\Microsoft Office\\OFFICE11\\EXCEL.exe Ausgabedatei.csv";
const char* prog=excel.c_str();
system(prog);
//Wobei der Name der Ausgabedatei vorher per append an den String angehängt wurde
```
Excel nicht geöffnet.


----------



## deepthroat (22. Juni 2007)

Seelenquell hat gesagt.:


> Könnte man mit
> 
> ```
> system("Excel.exe datei.csv");
> ...


Du mußt den Befehl der system Funktion genau so übergeben wie du es auch in der Kommandozeile tun würdest, da der Befehl vom Kommandozeileninterpreter ausgewertet wird. Zusätzlich mußt du natürlich noch die umgekehrten Schrägstriche entwerten.

```
string excel = "\"c:\\Programme\\Microsoft Office\\OFFICE11\\EXCEL.exe\" Ausgabedatei.csv";
```
So sollte es funktionieren. Allerdings wäre es evtl. besser einfach den start Befehl zu verwenden. Normalerweise sind .csv Dateien mit Excel als Standardprogramm verknüpft und somit würde "start Ausgabedatei.csv" Excel starten. Das Programm wird dann allerdings gleichzeitig ausgeführt, d.h. die system Funktion kehrt sofort zurück und dein Programm läuft weiter (und wird vermutlich gleich beendet).

Gruß


----------



## Seelenquell (22. Juni 2007)

Okay, ich weiß jetzt woran es hängt.
Beim Befehl

```
system("start c:\\Dokumente und Einstellungen\\Administrator\\Eigene Dateien\\Programme\\Prog2\\Logfile.txt);
```
stört er sich wohl an den Leerzeichen. Es erscheint die Fehlermeldung in Windows "Die Datei c:\Dokumente konnte nicht geöffnet werden".
Wenn ich die Leerzeichen wegmache kommt "Die Datei c:\DokumenteundEinstellungen\Adminstrator\Eigene konnte nicht geöffnet werden".

Durch was muss ich die Leerzeichen ersetzen, damit system() richtig mit dem Pfad umgehen kann ?

Danke schonmal!


----------



## Seelenquell (24. Juni 2007)

Habs immer noch nichts hinbekommen. Außerdem habe ich entdeckt, dass das Programm Eingabedateien mit Umlauten im Namen nicht öffnen will.

Noch jemand ne Idee ?


----------



## deepthroat (25. Juni 2007)

Seelenquell hat gesagt.:


> Okay, ich weiß jetzt woran es hängt.
> Beim Befehl
> 
> ```
> ...


Durch nichts. Wie schon gesagt mußt du den Pfad innerhalb des Strings in Anführungszeichen setzen.

```
system("start \"c:\\Dokumente und Einstellungen\\Administrator\\Eigene Dateien\\Programme\\Prog2\\Logfile.txt\"");
```



Seelenquell hat gesagt.:


> Habs immer noch nichts hinbekommen. Außerdem habe ich entdeckt, dass das Programm Eingabedateien mit Umlauten im Namen nicht öffnen will.


Ja, Umlaute und andere Sonderzeichen sollte man möglichst vermeiden.

Gruß


----------

