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
stringstreamconvert;// StringStream für die Konvertierung
2
convert<<temp;// Konvertierung
3
intitemp;// 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
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.
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.
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
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.
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:
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.
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
intargc
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.
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.
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,162: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.
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.