Forum: PC-Programmierung Konvertierung problem von string zu int


von Maxim (Gast)


Lesenswert?

Hallo Zusammen,

ich versuche daten aus einem txt_datei zu lesen.
Die daten sind nur int zahlen, die mit komma (,) getrennt sind.
Soweit so gut es klapptfast alles.
bei Konvertierung wird immer die 0 auch gelöcht.
hier einen abschnitt von dem code:
1
 stringstream convert;   // StringStream für die Konvertierung
2
 convert << temp;        // Konvertierung
3
 int itemp;           // des String
4
 convert >> itemp;       // nach int hier problem!!!!
5
 vec.push_back(itemp);
wie kann ich bei konvertieren verhindern, dass der 0 nicht gelöscht 
wird.


danke in voraus

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Maxim schrieb:
> bei Konvertierung wird immer die 0 auch gelöcht.

Welche Null, und was heißt "gelöscht"? Und wie stellst Du das fest?

von Maxim (Gast)


Lesenswert?

Das text datei beinhaltet solche zahlen:
1:0,1,12,46,78,19,16
2:0,63,78,99,120,150,186
3:0,66,190,45,123,468,5464,
4:0,235,253,526,245,125,1568,552
....
ich lese die Zahlen aus ein txt.datei und wollte die zeilenweise 
bearbeiten
und dann in eine andere txtdatei ausgeben.
Das ist allegmein mein vorhaben.


Nur wie mache ich das am besten?
Für jeden Vorschalgen herzlichen dank.

von Karl H. (kbuchegg)


Lesenswert?

Maxim schrieb:
> Das text datei beinhaltet solche zahlen:
> 1:0,1,12,46,78,19,16
> 2:0,63,78,99,120,150,186
> 3:0,66,190,45,123,468,5464,
> 4:0,235,253,526,245,125,1568,552

Und wenn du dir das mal genauer ansiehst, dann merkst du, dass das hier 
...

> Die daten sind nur int zahlen, die mit komma (,) getrennt sind.

... nicht korrekt ist.
Deine Zahlen werden durch Komma UND Doppelpunkte getrennt.

> Nur wie mache ich das am besten?

Wenn du zeilenweise lesen willst (was im übrigen oft eine gute Idee 
ist), dann eben mit getline in einen std::string einlesen, daraus einen 
Stringstream bauen und aus dem Stringstream die Einzelteile mittels >> 
herausholen. Ich seh da jetzt das Problem nicht. Abwechselnd in einen 
int und in einen char einlesen und dann teilt sich der String ganz von 
alleine in die Zahlen und die Trennzeichen zwischen den Zahlen auf.

In deinem Beispiel sind keine Leerzeichen oder sonstiges zwischen Zahlen 
und Trennzeichen. Wenn du dich darauf verlassen kannst, das das immer so 
ist, dann reicht bereits so ein einfaches vorgehen schon. Sonst müsste 
man geringfügig mehr Aufwand treiben, in dem man nach dem Einlesen einer 
Zahl in einer Schleife solange weitere einzelne char einliest, bis man 
wieder auf das Trennzeichen stösst.

Aber alles in allem: Wo liegt das Problem? Das ist ganz banales Einlesen 
von einem File - das Zerlegen in die Einzelteil ist von der 
allereinfachsten Sorte, weil du keine Spezialitäten brauchst sondern mit 
>> alles erledigen kannst.
Etwas aufwändiger wird dann die Fehlerbehandlung, aber das ist normal. 
Fehlerbehandlung verkompliziert Einleseroutinen und zwingt einen oft 
diese umzustellen und den geradlinigen Weg zu verlassen.

von Karl H. (kbuchegg)


Lesenswert?

Ich weiß zwar nicht, welche Bedeutung der ':' in den Daten hat, aber ich 
geh mal davon aus, dass das sowas wie ein Trenner ist, der die 
Datensatzidentifikation darstellt. D.h.
1
2:0,63,78,99,120,150,186
bedeutet:
Der Datensatz trägt die Identifikation 2 und zu diesem Datensatz gehören 
die Werte 0, 63, 78, 99, 120, 150, 186
1
#include <iostream>
2
#include <sstream>
3
#include <fstream>
4
#include <string>
5
#include <vector>
6
7
using namespace std;
8
9
struct dataSet
10
{
11
  int id;
12
  vector<int> values;
13
};
14
15
void inputData( const string& fileName, vector<dataSet>& result )
16
{
17
  ifstream input( fileName );
18
  string inputLine;
19
20
  while( getline( input, inputLine ) )
21
  {
22
    stringstream inStream( inputLine );
23
    int number;
24
    char delimiter;
25
    dataSet data;
26
27
    inStream >> data.id;
28
    inStream >> delimiter;
29
30
    while( inStream >> number )
31
    {
32
      data.values.push_back( number );
33
      inStream >> delimiter;
34
    }
35
36
    result.push_back( data );
37
  }
38
}
39
40
void outputData( const vector<dataSet>& data )
41
{
42
  for( size_t i = 0; i < data.size(); ++i )
43
  {
44
    cout << "Key: " << data[i].id << endl;
45
46
    cout << "  ";
47
    for( size_t j = 0; j < data[i].values.size(); ++j )
48
    {
49
      cout << data[i].values[j];
50
      if( j != data[i].values.size() - 1 )
51
        cout << ", ";
52
    }
53
    cout << endl;
54
  }
55
}
56
57
int main()
58
{
59
  vector<dataSet> allData;
60
61
  inputData( "c:\\kalle\\FileRead\\FileRead\\Data.txt", allData );
62
  outputData( allData );
63
}

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Und so sieht das dann mit rudimentärer Fehlerbehandlung bzw Behandlung 
von Leerzeichen/Tabs in den Datenzeilen aus.
Wie du siehst, immer noch nichts großartig Spezielles, sondern es wird 
alles mit einer Kombination aus getline und Anwendung des >> Operators 
zur Zerlegung der einen Zeile abgehandelt.

von immerUmständlich (Gast)


Lesenswert?

Warum hält sich sowas so hartnäckig?
Ein "normaler" stream kann genauso formatiert lesen wie ein 
stringstream.
Es ist ein Unding, Zeilenweise zu lesen und dann zu parsen. Warum nicht 
gleich?

Sieht dann genau so aus wie oben, nur das eben nicht eine Zeile gelesen 
wird, sondern aus dem inputstream (also hier input).

Am Besten ist es aber, falls es sich um feste Datensätze der Form
1
0:1,2,3,4,5,6
oder ähnlich handelt, dafür eine klasse zu schreiben, die den 
streamoperator >> überschreibt. Dann kann man Datensätze einlesen:
1
vector<datensatz> daten;
2
{
3
    datensatz temp_data;
4
    while(input >> temp_data)
5
    {
6
        daten.emplace_back(temp_data);
7
    }
8
}

Die klasse zu schreiben überlasse ich "Maxim".

von Karl H. (kbuchegg)


Lesenswert?

immerUmständlich schrieb:
> Warum hält sich sowas so hartnäckig?
> Ein "normaler" stream kann genauso formatiert lesen wie ein
> stringstream.
> Es ist ein Unding, Zeilenweise zu lesen und dann zu parsen. Warum nicht
> gleich?

weil es in Summe mit Fehlerbehandlung und Ausgabe von sinnvollen 
Fehlermeldungen sowie Wiederaufsatz des Einlesens für den nächsten 
Datensatz (wichtig wenn es darum geht ein Inputfile möglichst 
vollständig zu lesen um ALLE Fehler in den Daten auf EINEN BLick zu 
sehen und nicht 300 mal das Programm starten zu müssen um sich von einem 
Datenfehler zum nächsten zu hanteln) einfacher ist, zuerst eine Zeile 
als ganzes in einen String zu lesen und dann von diesem String weg die 
Zeile zu zerlegen.
Speziell dann, wenn eine Zeile ein Datensatz ist. Denn dann erledigt mir 
das getline die Zerlegung in Datensätze ganz von alleine.

> vector<datensatz> daten;
> {
>     datensatz temp_data;
>     while(input >> temp_data)
>     {
>         daten.emplace_back(temp_data);
>     }
> }

Mach die Zerlegung in Datensätze, bau Fehlerbehandlung ein und du wirst 
draufkommen, dass dieses direkte Lesen auch nicht einfacher oder 
übersichtlicher ist, als zuerst in einen string einzulesen und dann von 
dort weiterzuarbeiten.


> Die klasse zu schreiben überlasse ich "Maxim".
Lass ihn erst mal mit konventioneller Denke klar kommen.
Oder zeig ihm wies geht. Das DU das kannst, glaub ich dir ungesehen.
Hilft aber dem Fragesteller nicht viel, wenn du 3 Schritte überspringst 
und deine Antwort nicht an das anpasst, was er bereits so einigermassen 
weiß bzw. nicht weiß. Von jemanden der mit regulären Stream Operationen 
nicht klar kommt, kannst du nicht erwarten, dass er mit Operator 
Overloading klar kommt.

von immerUmständlich (Gast)


Lesenswert?

Wenn du eine Zeilennummer willst, dann zähl halt mit.
Was meinst du wie der Compiler das macht? Der liest auch nicht 
Zeilenweise, das hier kompiliert wunderbar:
1
#include <stdio.h>
2
3
int
4
5
               main
6
(
7
      int argc
8
,
9
char*
10
argv[
11
]
12
)
13
{
14
15
}

Es geht auch nicht darum, dass es einfacher ist, sondern weniger 
aufwändig.
Einen stringstream zu konstruieren, ist vergleichsweise aufwändig. Bevor 
du mir mit dem Argument kommst, ist doch egal, heutige Computer haben 
genug Rechenleistung:
Mag ja sein, aber warum es einfach offensichtlich umständlicher machen, 
als es sein muss? Das da oben ist halt C-style, ist vielleicht dem Forum 
geschuldet, weiß ich nicht.
In C++ gibts Klassen und Operatorüberladung, diese eignen sich hier 
wunderbar.

Das problem ist außerdem: Du bist bei deinem Design auf Zeilen 
festgelegt.
Was, wenn man noch Kommentare oder ähnliches einbauen möchte?
1
1:1,5,
2
    20,32,0 # Start of segment one#
3
2: (usw)
4
5
#oder mehrzeilig, dabei muesste man ein whitespace ohne zusaetzliches trennzeichen (: ,) als datensatztrenner erkennen oder noch einen einbauen#
6
1:42,-36,
7
32, 64,  90   0:13, (usw)
Zugegeben, dieses ganze format ist auch eher auf Zeilen ausgelegt, aber 
trotzdem ist einfach generell schlechte Praxis (meiner Meinung nach) 
erstmal in Zeilen zu denken. Man sollte, falls möglich genau das 
Gegenteil tun. Wenn man die daten von einem Controller oder so bekommnt, 
muss man dann nicht extra ein newline senden.
Sollte es nicht anders gehen, na gut, dann halt zeilenweise.

von immerUmständlich (Gast)


Lesenswert?

In Bezug auf das Schreiben der Klasse hast du Recht.
@Maxim: ich poste gleich noch was.

von Karl H. (kbuchegg)


Lesenswert?

immerUmständlich schrieb:
> Wenn du eine Zeilennummer willst, dann zähl halt mit.
> Was meinst du wie der Compiler das macht? Der liest auch nicht
> Zeilenweise,

doch, das tun sie. die meisten. wenn sie in der Fehlermeldung die 
komplette Zeile ausgeben wollen, in der der Fehler festgestellt wurde.
Das Lesen erfolgt in einen Zwischenbuffer der eine oder mehrere Zeilen 
hält. Dort setzt dann der lexikalische Parser auf und liefert 
Input-Token um Input-Token. Bei Bedarf kann dann der Compiler für 
Fehlerausgaben auf die Zeile zurückgreifen, die gerade in Arbeit ist.

> das hier kompiliert wunderbar:

Ähm. Spätestens jetzt ist der Ofen aus. Was bitte hat freie Formatierung 
damit zu tun, ob man sich eine komplette Zeile von einem File holt oder 
nicht?

> Es geht auch nicht darum, dass es einfacher ist, sondern weniger
> aufwändig.

Mach es vollständig!
Es ist simpel zu sagen, dass etwas einfacher ist, wenn man den Beweis 
nicht antritt.

> Das problem ist außerdem: Du bist bei deinem Design auf Zeilen
> festgelegt.

Ich orientiere mich erst mal an dem, was ich über das Fileformat weiß 
bzw. was ich vom TO präsentiert bekomme.

> Was, wenn man noch Kommentare oder ähnliches einbauen möchte?

Dann macht man das anders. Aber noch besteht dieses Problem nicht.

> Gegenteil tun. Wenn man die daten von einem Controller oder so bekommnt,
> muss man dann nicht extra ein newline senden.

E ist völlig wurscht, ob du vom Controller irgendein Trennzeichen oder 
eben ein Newline als Trenner der Datensätze schicken musst. Denn ein 
Trennzeichen brauchst du. Denn wie willst du sonst  wissen, dass
1
1:0,1,12,46,78,19,162:0,63,78,99,120,150,186
zwischen 16 und 2 ein neuer Datensatz anfängt?
Egal wie du es drehst, da muss ein Trenner rein
1
1:0,1,12,46,78,19,16,2:0,63,78,99,120,150,186
und ob der jetzt ',' oder Newline ist, ist prinzipiell Jacke wie Hose. 
Einfacher ist aber der newline, weil er den nächsten Datensatz ankündigt 
und man nicht darauf angewiesen ist, anhand des ':' erst im Nachhinein 
den neuen Datensatz zu erkennen.

Will man die Zeilenstruktur auflösen und freie Formatierung zulassen, 
dann muss man sowieso den Aufwand erhöhen. Das steht ausser Frage. Aber 
dann sieht auch der ganze Aufbau etwas anders aus.

> Sollte es nicht anders gehen, na gut, dann halt zeilenweise.

Es geht nicht um 'anders gehen'. Natürlich geht es auch anders.
Es geht darum, dass erst mal in einen String einlesen ein probates 
Mittel ist, wenn einem Fehlerbehandlung NICHT egal ist. Denn alle 
Einlesefunkionen kranken grundsätzlich an 2 Dingen
* die Operation bricht einfach ab, ohne das man weiß warum.
  Lese ich in einen int ein und werfe dem Teil die 'Zahl' 453z87 vor,
  dann bricht die Einleseoperation mitten drinn ab.
  Das wäre noch so einigermassen verschmerzbar, denn ich weiß ja, das
  als nächstes ein Delimiter kommen müsste und 'z' ist nun mal kein
  gültiger Delimiter
* schlimmer aber: Das Überspringen des fehlerhaften Datensatzes, das
  Neusynchronisieren mit dem nächsten Datensatz bedeutet für mich
  als Programmierer Aufwand.


> Einen stringstream zu konstruieren, ist vergleichsweise aufwändig.

Lass das mal das Problem des Compilers sein. In einem Programmteil, der 
I/O lastig ist, ist es müssig, sich über kleinere Dinge den Kopf zu 
zerbrechen, die sich im Speicher abspielen.

von immerUmständlich (Gast)


Lesenswert?

Ok, es ging mir ganz und gar nicht darum, hier eine große Diskussion 
loszubrechen. Ist ja auch prinzipiell nicht so wild, ist halt auch meine 
persönlich Vorliebe.
Und das Programmbeispiel ist natürlich Schwachsinn, ich wollte nur 
verdeutlichen, dass das nicht Zeilenweise orientiert ist.

Bei
1
1:0,1,12,46,78,19,16 2:0,63,78,99,120,150,186
 kann man schon erkennen, wo das ende ist, kannst du und ich ja auch.
Man beachte das Leerzeichen ohne komma.
Ist halt umständlich so zu machen, hast ja recht.

Hier der Code, ist nicht wirklich fertig und nicht ganz fehlerfrei. Hab 
schon ne Weile nicht mehr C++ programmiert, deshalb fehlt das aufräumen 
des streams (momentan kann man einen Datensatz korrekt einlesen, der 
stream ist aber trotzdem "gefailed"). Nicht schön.
Muss jetzt aber los.

Wollte hier auch niemanden auf die Füße treten, die anderen Post waren 
eventuell etwas harsch formuliert, entschuldigung hierfür.
1
// datensatz.hpp
2
#include <vector>
3
#include <istream>
4
#include <ostream>
5
6
class datensatz
7
{
8
  private:
9
    int id;
10
    std::vector<int> values;
11
  public:
12
    friend std::istream& operator>>(std::istream& stream, datensatz& data);
13
    friend std::ostream& operator<<(std::ostream& stream, const datensatz& data);
14
};
15
16
// datensatz.cpp
17
#include "datensatz.hpp"
18
19
#include <iostream>
20
21
using std::cout;
22
23
std::istream& operator>>(std::istream& stream, datensatz& data)
24
{
25
  // neuer Datensatz, der alte wird erst bei vollstaendigem korrekten Lesen
26
  // aktualisiert
27
  datensatz temp;
28
29
  char delim = 0;
30
  stream >> temp.id;
31
  if(!stream)
32
  {
33
    // error
34
    // eventuell exception werfen oder aehnliches
35
  }
36
37
  stream >> delim;
38
  if(delim != ':')
39
  {
40
    // error
41
    // eventuell exception werfen oder aehnliches
42
  }
43
44
  int num = 0;
45
  while(stream >> num)
46
  {
47
    temp.values.emplace_back(num);
48
    stream >> delim;
49
    if(delim != ',') // fertig gelesen
50
    {
51
      // letzten Zustand wiederherstellen
52
      stream.clear();
53
      break;
54
    }
55
    delim = 0;
56
  }
57
  data = temp;
58
59
  return stream;
60
}
61
62
std::ostream& operator<<(std::ostream& stream, const datensatz& data)
63
{
64
  stream << data.id << ':';
65
  for(unsigned int i = 0; i < data.values.size() - 1; ++i) // -1, das letzte Element will sonderbehandlung
66
  {
67
    stream << data.values[i] << ',';
68
  }
69
  stream << *(data.values.end() - 1);
70
  return stream;
71
}

von Maxim (Gast)


Lesenswert?

Danke an alle
Es hat alle gut geklappt
Herzlichen Dank

von Maxim (Gast)


Lesenswert?

Ich habe noch eine Frage:
es geht um folgenden:
Ich habe mehrere vektoren v1,v2,v3,v4,......v20.
Diese Vektoren haben unterschiedliche lange und beinhalten nur zahlen.
Die Vektoren gehören einen bestimmte Gruppen zB:
Gruppe A beinhaltet:
v1,v2,v3,v4...
Gruppe B :v11,v22,v33,v44....
Gruppe C: v111,v222,v333,v444.....

Die Vektoren sind auf Gruppen aufgeteilt.

Ich möchte in jede Gruppe nach gleiche Datensätze suchen:
ZB:
bei Gruppe A:
die beinhaltet: v1,v2,v3,v4....
ich möchte alle Vektoren in diese Gruppe durchsuchen, ob Sie die gleiche 
Datensatz beinhalten?

Meine Idee ist:
alle Vektoren, die eine Gruppe gehören in einem einzeln Vektor 
V_Gruppe_A kapseln und nur diese Vektor dann durchsuchen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.