Zum Inhalt springen

[VC++] Probleme mit wifstream (lesend)


Gateway_man

Empfohlene Beiträge

Hallo,

zu erst einmal eine Frage vorweg.

Ist der wifstream obsolet oder hoch kritisch?

Ich finde total wenig wenn ich im Netz nach meiner Problematik suche. Meistens finde ich dann Beispiele mit ifstream...

Ich will mit dem wifstream immer gebuffered den Inhalt einer Datei auslesen.

Also nicht alles auf einmal sondern man soll der Funktion eine Buffersize mitgeben können.

Das ganze sieht wie folgt aus:


		XCore* XCore::LoadFile(const std::wstring& filename,const int bufferSize = 4096)

		{

			XCore* result = 0;


			std::wifstream wif(filename);

			wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));

			int iretval = 0;


			while (!wif.eof())

			{

				wchar_t *_buffer = new wchar_t[bufferSize];

				iretval = wif.read(_buffer, bufferSize).gcount();

				if (bufferSize != iretval)

				{

					wchar_t * wctemp = new wchar_t[iretval];

					wcsncpy(wctemp, _buffer, iretval);										

					delete[] _buffer;

					_buffer = wctemp;

				}		

				delete[] _buffer;


			}

			wif.close();

			return result;

		}

Nicht wundern. Ich wollte die Funktion erstmal per Debugger testen, bevor ich mit dem Inhalt weiter verfahre.

Und schon hatte ich einen Fehler gefunden.

Mein Problem ist das gcount() immer falsche werte zurückliefert.

Momentan läuft er immer in die Verzweigung rein die das Array resizen soll.

Leider wird das Temporäre Array immer zu groß definiert.

Sieht jemand jetzt schon einen gravierenden Fehler?

Ansonsten würd ich mit weiteren Infos aufwarten.

LG

Gatway

Link zu diesem Kommentar
Auf anderen Seiten teilen

Sieht jemand jetzt schon einen gravierenden Fehler?
Mehrere, aber keine, die das Verhalten erklären, das ich übrigens mit Visual C++ 2012 nicht reproduzieren kann.

Dein Code erkennt nicht, ob die Datei überhaupt geöffnet werden konnte.

Eof eignet sich nicht als Schleifenbedingung, weil es erst dann true liefert, wenn schon ein Lesevorgang fehlgeschlagen ist. Wenn du eine Leseschleife brauchst, nimm die Leseoperation selbst als Schleifenbedingung.

Benutz kein new[] für den Puffer, sondern std::vector.

Aber überhaupt ist die ganze Vorgehensweise ziemlich C-artig, wenn ich so sagen darf.

Wenn du ohnehin die ganze Datei einlesen willst, brauchst du keine Schleife:


std::wifstream wif(filename);
wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
std::vector<wchar_t> v;
std::copy(std::istream_iterator<wchar_t, wchar_t>(wif), std::istream_iterator<wchar_t, wchar_t>(), back_inserter(v));
// oder wenn du es als string brauchst:
std::wstring s(v.begin(), v.end());
[/code]

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi,

danke für die schnelle Antwort.

Vielleicht ein kurz ein paar Informationen warum ich das so gemacht habe.

Warum lese ich nicht die ganze Datei ein?

Die Zieldatei kann mehrere GB groß sein und wenn es fertig ist, wird die Funktion & deren "Analysefunktionen" in einem eigenen Thread ausgelagert.

Es soll dann die Möglichkeit existieren den Vorgang abzubrechen wenn es zu lange dauert. Wenn ich aber ein komplettes Read mache, wüsste ich nicht wie ich das sauber abbrechen könnte.

Zusätzlich kann es sein das das lesen der Datei selbstständig abgebrochen wird, da er abbrechen soll, wenn er bei der Analyse einen Strukturfehler findet.

Deswegen iteriere ich durch.

Am Ende wird in der Schleife dann noch eine Ausstiegbedingung reinkommen.

Ein vector habe ich deswegen nicht genommen, da der eingelesene Inhalt in einer anderen Funktion aufbereitet wird (viele wchar_t character werden rausgelöscht).

Soweit ich das verstanden habe, ist ein vector nicht dafür geeignet bzw. ist sehr langsam wenn es viele Inhalte gibt und diese auch öfters entfernt oder verändert werden.

Wenn überhaupt dann würde ich evtl als alternative ne std::list hernehmen. Allerdings kann ich da nicht auf den Index zugreifen und müsste tatsächlich immer durchiterieren.

Hm ich war der Meinung das char Unicode nicht handlen kann. Meines wissen ist der Wertebereich eines chars -127 bis 127. Auch ein unsinged char (255) kann ja nichtmal ansatzweise die UTF8 geschweige denn die UTF16 Tabelle abbilden.

Da müsste ich dann ja immer mit zwei charactern arbeiten um ein Zeichen abzubilden.

Das ist mir zuviel Aufwand. Da nehm ich lieber gleich den Typ der dafür vorgesehn ist.

Ich habe mal ein Screenshot zusammengebastelt um das Problem genauer darzustellen.

post-47975-1443044970469_thumb.png

Ich kann mir das ehrlich gesagt nicht erklären.

LG

Gateway

PS:

@Klotzkopp:

Zudem verlangen die ganzen tollen wc Funktionen immer Arrays. Da dachte ich, bleib ich lieber gleich bei Arrays, anstatt mir jedesmal vom Container ein Array zu holen.

Bearbeitet von Gateway_man
Link zu diesem Kommentar
Auf anderen Seiten teilen

Wo ist denn da das Problem?

Deine Daten sind nicht nullterminiert, das weiß aber der Debugger nicht. Darum stellt er auch den Speicherinhalt hinter deinem Text dar.

Das am ende sind keine 0 Werte. Die letzten werte im Array sind 65021.

Was mich stört ist das das Array größer ist als der eigentliche Inhalt.

Ich hatte die Hoffnung, das ich das Array so resizen kann, das nur noch der relevante Inhalt drin ist.

Muss ich das im nachhinein nochmal durchiterieren und händisch rausschmeißen?!

wctemp[38]

65021 L'﷽'

wctemp[37]

65021 L'﷽'

wctemp[36]

179 L'³'

wctemp[35]

126 L'~'

wctemp[39]

43947 L'ꮫ'

wctemp[40]

43947 L'ꮫ'

So kann ich es nicht lassen, sonst steigt meine Routine aus, welche die Datenstruktur validiert, weil sie 65021 als invalid erachtet.

LG

Gateway

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hm ich war der Meinung das char Unicode nicht handlen kann. Meines wissen ist der Wertebereich eines chars -127 bis 127. Auch ein unsinged char (255) kann ja nichtmal ansatzweise die UTF8 geschweige denn die UTF16 Tabelle abbilden.

Da müsste ich dann ja immer mit zwei charactern arbeiten um ein Zeichen abzubilden.

Das ist mir zuviel Aufwand. Da nehm ich lieber gleich den Typ der dafür vorgesehn ist.

siehe Boost.Locale: Recommendations and Myths

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ein Array ist so groß, wie du es anlegst.

Kann es sein, dass du die Daten einfach nur mit einer Funktion weiterverarbeitest, die von Nullterminierung ausgeht?

Ja. Ich kenne aber auch keine Funktion die nicht von einer Nullterminierung ausgeht. Ich meine woher soll die Funktion dann wissen wann das Ende der Zeichenkette erreicht ist.

Ich versteh nicht warum am Ende kein \0 ist.

Ich meine nach meinem Verständnis muss doch eigentlich jede Zeichenkette eine Terminierung haben sonst kann man nicht sinnvoll damit arbeiten. Woher soll xyz Funktion wissen, wann die Zeichenkette zu Ende ist?!

Ich hab versucht das ganze von Anfang an abzufangen. Also das ursprungsarray schon mit der richtige größe zu erstellen, wenn die Dateigröße kleiner ist als die Buffersize.


			std::wifstream wif(filename);

			if (wif.is_open())

			{

				wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));

				int iretval = 0, fileSize = -1;

				wif.seekg(0, std::ios::end);

				fileSize = wif.tellg();

				wif.seekg(0);

				if (fileSize > 0 && fileSize < bufferSize)

				{

					wchar_t *_buffer = new wchar_t[fileSize];

					wif.read(_buffer, fileSize);

                                        //Do Something

					delete[] _buffer;

				}

				else

				{

					while (!wif.eof())

					{

						wchar_t *_buffer = new wchar_t[bufferSize];

			 			iretval = wif.read(_buffer, bufferSize).gcount();

						if (bufferSize != iretval)

						{


							wchar_t * wctemp = new wchar_t[iretval];

							wcsncpy(wctemp, _buffer, iretval);										

							delete[] _buffer;

							_buffer = wctemp;

						}		

						delete[] _buffer;


					}

				}

				wif.close();			

			}

Aber das hat mir nichts gebracht....

LG

Gateway

Link zu diesem Kommentar
Auf anderen Seiten teilen

Verstehe gerade nicht was du mir damit sagen willst. Ich weiß auch das es geht. Aber es ist umständlich.

Hier ein Beispiel:

Ich iteriere ein char Array durch um nach diversen sequenzen zu suchen.

wenn ich jetzt prüfen möchte ob der aktuelle character ein match ist, geht das nicht so einfach:


if (currentCharacter == 0xe0b8bf)

{

   // hier wird er nie reingehn. Die Bedinung kann nie erfüllt werden, weil der Wertebereich nie groß genug sein wird.

   // ich hab im Netz schon obskure Lösungswege gesehn. Bis hin zu einer Funktion die immer zwei character eingelesen hat und diese dann miteinander mulitpliziert hat. Aber das war mir zu strange und zu Aufwändig.

}

Das sah für mich einfach nach zu viel rum gefriggel aus. Daher wchar_t. Da hab ich auch den Vorteil das ich easy auf utf-32 wechseln kann. Und auf fremde Libraries greif ich nur sehr ungern zurück. Also ist Boost jetzt nicht wirklich für mich eine alternative.

Bearbeitet von Gateway_man
Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich iteriere ein char Array durch um nach diversen sequenzen zu suchen.

wenn ich jetzt prüfen möchte ob der aktuelle character ein match ist, geht das nicht so einfach:

Wieso soll das nicht gehen? Dafür gibt es Regulärer Ausdruck

Das sah für mich einfach nach zu viel rum gefriggel aus. Daher wchar_t.

Dein Code sieht für mich danach aus, denn wenn ich Pattern matchen will, nehme ich reguläre Ausdrücke mit einer entsprechenden Bibliothek.

Da hab ich auch den Vorteil das ich easy auf utf-32 wechseln kann. Und auf fremde Libraries greif ich nur sehr ungern zurück. Also ist Boost jetzt nicht wirklich für mich eine alternative.

Man braucht dafür nicht die Boost, nur die Erklärung ist bezüglich UTF-8 bzw ITF-16 etc durchaus sinnvoll.

Dein Code sieht nach einer Frickellösung aus, ich würde blockweise einlesen und jeden Block mittels regulärer Ausdrücke verarbeiten, wenn man es richtig macht, dann geht das sogar mittels Threads, denn es ist nur ein lesender Zugriff. Außerdem sind new und delete mit die laufzeittechnisch die teuersten Aufrufe, d.h. das in Deiner inneren Schleife zu machen, wird die Performance definitiv einbrechen lassen. Gerade in Bezug auf der Blockverarbeitung verweise ich noch mal auf Klotzkopps Kommentar bwzügl der Null-Termination.

Link zu diesem Kommentar
Auf anderen Seiten teilen

hallo,

@flashpixx:

Ich glaube wir beide reden aneinander vorbei. Ich weiß ehrlich gesagt beim besten willen nicht, was ich jetzt mit regex anfangen soll :confused:.

Danke für deine Hilfe, aber ich versuch mich jetzt wieder an meinem ursprünglichen Problem.

Aus reiner Neugier. Beinhaltet die stl überhaupt ein regex Konstrukt?

@Klotzkopp:

Danke für deinen Hinweis mit der Funktion wcsncpy. Tatsächlich ist es die Funktion die den Inhalt ans Ende hängt.

Was ich aber jetzt noch nicht raffe ist, es scheint kein aber keine string Terminierung zu sein. Ich hab den Inhalt nach '\0' durchsuchen lassen ohne Erfolg. Ich raff das nicht.

Warum sieht das konstante Array der std::wstring korrekt aus und am unteren Array hängt wieder der mist dran.

post-47975-14430449705412_thumb.png

Also für mich sieht der Schwachsinn am Ende nicht nach einer Null Terminierung aus.

Ich hab mir eine eigene "copy" Funktion geschrieben, in der Hoffnung das sich das dann erledigt hat.


		wchar_t * wccopyex(const wchar_t * input, const size_t index, const size_t count, bool wcsterminate)

		{

			wchar_t * result = 0;

			if (input != 0)

			{

				int ia = std::wcslen(input);

				if (index + count <= ia)

				{

					result = new wchar_t[(wcsterminate ? (count + 1) : count)];

					for (int i = 0; i < count; i++)

					{

						result[i] = input[index + i];

					}

					if (wcsterminate)

						result[count] = '\0';

				}

			}

			return result;

		}

Das hat wie man im Screenshot sieht nicht geklappt. Stellt sich raus das er die letzten paar Indexe schon bei der Initialisierung des Arrays setzt. Mir ist gerade aufgefallen, das das result Array auch 40 Elemente hat, was spannend ist, da das eingabe Array lediglich 32 Elemente hat. Ich werde noch irre.

Ich flipp echt bald noch aus.

Ich will doch lediglich ein nicht konstantes wchar_t Array. Ist das denn zu viel verlangt. Einen ganzen Tag beschäftigt mich der Unfug jetzt schon....

Etwas so triviales das in jeder anderen Sprache in einem drei Zeiler gemacht werden kann und kein absonderliches verhalten verursacht.

Quasi die ganze Logik der dahinter liegenden Library arbeitet mit wchar_t Arrays. Aber so brauch ich nichtmal versuchen den Mist zu übergeben.......

Ich geh jetzt mal mein Rechner gegen die Wand werfen...

LG

Gateway

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum sieht das konstante Array der std::wstring korrekt aus und am unteren Array hängt wieder der mist dran.

Du hast da einen Zeiger, kein Array. Der Debugger geht bei char- (und verwandten) Zeigern davon aus, dass da nullterminierte Daten drin sind. Darum zeigt er den Inhalt bis zur Nullterminierung an. Wenn keine drin ist, wird der Speicherinhalt dahinter mit angezeigt.

Beim wstring passiert das nicht, weil der eine eigene Längeninformation trägt, die der Debugger auswertet.

Häng dich nicht so sehr daran auf, was dir der Debugger anzeigt.

Ich hab mir eine eigene "copy" Funktion geschrieben, in der Hoffnung das sich das dann erledigt hat.
Was meinst du, wie wcslen die Länge ermittelt? Über die Nullterminierung. Wenn da keine ist, hilft dir auch wcslen nicht. Du musst dir einfach selbst merken, wie lang deine Daten sind. Oder eben nicht mit rohen Zeigern und Arrays hantieren. Bearbeitet von Klotzkopp
Link zu diesem Kommentar
Auf anderen Seiten teilen

Beim wstring passiert das nicht, weil der eine eigene Längeninformation trägt, die der Debugger auswertet.

Ah okay. Aber auch wstring muss ja wissen wann die Zeichenkette endet. Wie ermittelt wstring denn die gültige Länge? Denn anscheinend kriegt wstring das richtig hin.

Ich habe es übrigens auch statt mit wcslen mit der length Funktion des wstrings erfolglos probiert.

Häng dich nicht so sehr daran auf, was dir der Debugger anzeigt.

Das sagst du so einfach ;). Das ist die einzige Validierungsmöglichkeit die ich habe. Meinst du das die Debugger Information falsch ist und ich es ignorieren kann?

Was meinst du, wie wcslen die Länge ermittelt? Über die Nullterminierung. Wenn da keine ist, hilft dir auch wcslen nicht. Du musst dir einfach selbst merken, wie lang deine Daten sind. Oder eben nicht mit rohen Zeigern und Arrays hantieren.

Hm. Wie macht wstring das. Der muss ja auch irgendwie die länge ermitteln?!

Was ich übrigens sehr spannend finde ist:

Wenn ich anstelle von wcsncpy wcscpy verwendet, zeigt er es zumindest im Designer richtig an.

Ich finde das echt total irritierend.

Mit deiner vektorlösung und dem wstring funktioniert es. Versuche ich dann aber irgendwie an einen nicht konstanten wchar_t Pointer zu gelangen funktioniert es nicht. Ich greife doch auf die selbe Datei zu.

LG

Gateway

PS:

@Klotzkopp:

Vielen Dank für deine Hartnäckigkeit und dein Geduld :D. Jetzt hab ich es endlich gerafft (hat ja auch lang genug gedauert) und hab auch eine Patentlösung :) und muss auch nicht auf meine wchar_t Pointer verzichten. (Ja ich weiß ich sollte die Finger davon lassen, aber es gibt gewisse Abhängigkeiten)

Jetzt versteh ich auch wie du das mit dem Debugger gemeint hast :upps.

Danke

Interessant wäre es dennoch wie wstring die länge ermittelt, sofern du dahingehend was weißt :).

Bearbeitet von Gateway_man
Link zu diesem Kommentar
Auf anderen Seiten teilen

Wie macht wstring das. Der muss ja auch irgendwie die länge ermitteln?!
Das kommt auf den Konstruktor an. Es gibt einen für wchar_t-Zeiger, der nimmt natürlich auch wcslen. Der Konstruktor, den ich oben benutzt habe, verwendet zwei Iteratoren. Die Länge ist damit die Anzahl der Schritte dazwischen.

Wenn ich anstelle von wcsncpy wcscpy verwendet, zeigt er es zumindest im Designer richtig an.
Das liegt daran, dass wcscpy einfach alles kopiert und auf jeden Fall eine Nullterminierung anhängt. wcscpy nimmt aber keine Rücksicht auf die Größe des Zielspeichers und erwartet nullterminierte Quelldaten. Bearbeitet von Klotzkopp
Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...