C++ bestimmte anzahl an chars aus textdatei lesen funktioniert nicht richtig

striker159

Lt. Junior Grade
Registriert
Dez. 2008
Beiträge
327
hallo.
ich habe folgenden kurzen code ausschnitt, mit dem ich 10 zeichen aus einer datei lesen will.
input.txt : abcdefghijklmnop

mat8z6vz.jpg

es werden zwar 10 zeichen gelesen, allerdings folgt danach noch datenmüll, wie man auf der konsole und im debugger sehen kann.

kann mir jemand sagen, wie ich das problem beheben kann?
 
Allokiere und initialisiere doch mal 11 chars, lies aber nur 10 aus der Datei... könnte helfen ;)
 
Du überschreibst das komplette Array inllusive des letzten Nullbytes. Also macht cout weiter, bis es eins findet.
 
Wenn einzelne Chars eine Zeichenkette (String) bilden sollen, muss das Ende mit dem Wert 0 (Null) gekennzeichnet werden. Folglich muss der Speicherplatz für eine Zeichenkette auch um ein Element größer sein als die Anzahl der Zeichen, die in ihr enthalten sind. Somit braucht man für eine Zeichenkette der Länge zehn auch genau 11 Chars - am Ende muss ja noch '\0' angefügt werden.
 
ok, ich habe es gerade in einer linux vm probiert, dort funktioniert es mit 11 chars, und dann 10 einlesen.
danke.

visual studio will das bei mir nicht richtig machen >_>, da muss ich mich mal drum kümmern.
 
Fellor schrieb:
Versuch mal an der Stelle char[10] ein '\0' zu setzen.

Darf Du nicht, weil diese Speicherstelle gar nicht alloziert ist.
Bei z.b. array[10], fängt das bei array[0] an und hört bei array[9] auf, also insgesamt 10 Stück!

Wenn du 10 Zeichen im string haben willst muss du dir platz für 11 Zeichen holen, weil das
letzte Byte immer ein Null-Byte ist.

Du müsstest also new char[11] machen.
Aber auch meset (..,.., sizeof(char) * 11) machen.
Dann 10 Zeichen lesen.
Dann sollte das gehen.
 
striker159 schrieb:
visual studio will das bei mir nicht richtig machen >_>, da muss ich mich mal drum kümmern.
Nein. Du willst es nicht richtig machen und GCC gibt dir aus reinem Zufall das Ergebnis, das du gern hättest.
 
Versuch es mal mit strings, anstatt chars

Code:
#include <string>
#include <iostream>
#include <fstream>
...

std::ifstream in(filename, std::ios::in);

std::string contents;
in.read(&contents[0], 10);
in.close();
std::cout << contents << std::endl;
...
 
RayKrebs schrieb:
Darf Du nicht, weil diese Speicherstelle gar nicht alloziert ist.
Bei z.b. array[10], fängt das bei array[0] an und hört bei array[9] auf, also insgesamt 10 Stück!

Wenn du 10 Zeichen im string haben willst muss du dir platz für 11 Zeichen holen, weil das
letzte Byte immer ein Null-Byte ist.

Du müsstest also new char[11] machen.
Aber auch meset (..,.., sizeof(char) * 11) machen.
Dann 10 Zeichen lesen.
Dann sollte das gehen.

Meinte ich doch eigentlich auch >_>
Ging doch nur um's Prinzip.

Wäre aber eine [10] entsprechen 0,1,2,3,4,5,6,7,8,10 also 11 Stellen? Ist lange her, bin mir nicht mehr sicher.

Edit: Hast natürlich recht, sind 10 Stellen.
 
Zuletzt bearbeitet:
Hancock schrieb:
@myaccount: Genauso NICHT. Wie groß ist dein String und wo wird dir garantiert, dass die Zeichen hintereinander im Speicher liegen?
Wenn schon Container, dann richtig (in dem Fall z.B. ein std::vector mit resize).
Wie schon angemerkt, 11 Byte holen und 11. aus 0 setzten und nur 10 einlesen.

Und wo ist das Problem ?

std::string allokiert genausoviel platz wieviel der braucht für den Inhalt. Du kannst den Speicherbedarf aber nachträglich vergrössern/verkleinern mit resize(), capacity() oder wie auch auch immer, wenn es sein muss.
Die Länge von dem gelesenen Inhalt bekommst du mit length() bzw size() zurück.
In der Regel liegt der Speicherplatz am Stück im string intern, wenn nicht (bei resizen usw), dann ist doch auch kein Problem, die Zeichen werden aber in jedem Fall in sicherer Reihenfolge liegen, wie die gelesen wurden.

Vector nutzt man für andere Sachen. Für dieses Problem den zu nutzen wäre es möglich, aber nicht sinnvoll.
 
Zumindest mit aktuellem C++ sollte das übergeben per &contents[0] tatsächlich funktionieren:

http://en.cppreference.com/w/cpp/string/basic_string schrieb:
The elements of a basic_string are stored contiguously, that is, for a basic_string s, &*(s.begin() + n) == &*s.begin() + n for any n in [0, s.size()), or, equivalently, a pointer to s[0] can be passed to functions that expect a pointer to the first element of a CharT[] array. (Since C++11)
 
Zuletzt bearbeitet:
Das Problem ist, du schreibst in deinen String mit der Größe 0 die Daten rein => wieder Speicherüberlauf, diesmal mglw. unbemerkt, da der String leer erscheint (da .length()=0).
Das in C++11 mittlerweile strings garantiert kontinuierlich sind, wusste ich nicht. Dann geht es natürlich, wenn man vorher die Größe anpasst.
 
Hancock schrieb:
Das Problem ist, du schreibst in deinen String mit der Größe 0 die Daten rein => wieder Speicherüberlauf, diesmal mglw. unbemerkt, da der String leer erscheint (da .length()=0).
Das in C++11 mittlerweile strings garantiert kontinuierlich sind, wusste ich nicht. Dann geht es natürlich, wenn man vorher die Größe anpasst.

Du musst nichts anpassen, der String wird bei Bedarf automatisch seinen Speicherbedarf allokieren.

Wenn du folgendes machst:

Code:
std::string a;
a = "Hello World";

Dann würde es nach deiner Aussage auch nicht funktionieren, was aber falsch wäre.

Wie dem auch sein.... Code was ich oben gepostet habe, ist valid und sollte so funktionieren, ist auch in Spezifikation von C++ Standard so. So weit ich weiss, wird am Ende vom String beim read() auch automatisch ein '\0' gesetzt, deswegen braucht man in Vergleich zu char[] das nicht zu machen.
 
myaccount schrieb:
Du musst nichts anpassen, der String wird bei Bedarf automatisch seinen Speicherbedarf allokieren.

Wenn du folgendes machst:

Code:
std::string a;
a = "Hello World";

Dann würde es nach deiner Aussage auch nicht funktionieren, was aber falsch wäre.

Wie dem auch sein.... Code was ich oben gepostet habe, ist valid und sollte so funktionieren, ist auch in Spezifikation von C++ Standard so. So weit ich weiss, wird am Ende vom String beim read() auch automatisch ein '\0' gesetzt, deswegen braucht man in Vergleich zu char[] das nicht zu machen.


Dein zweites Beispiel ist was VÖLLIG anderes als das erste. Im ersten greifst du via den []-Operator mit Index 0 auf das nullte Element eines String-Objekts zu, das zu diesem Zeitpunkt überhaupt keine Elemente hat. Allein das ist schon falsch.
Die nächste falsche Annahme ist, dass der String in diesem Beispiel sich einfach automatisch vergrößert, wenn das nötig ist. Wie soll das denn gehen?? Die read()-Methode des ifstream-Objekts kriegt nur einen char-Pointer. Die Methode hat keine Ahnung, ob diese Adresse in das interne Array eines String-Objekts oder einfach nur auf ein nacktes C-Style Character-Array zeigt. Folglich hat sie auch überhaupt keine Möglichkeit, hier irgend was zu vergrößern.

Im zweiten Beispiel gehst du über den Operator = von std::basic_string ... der ist tatsächlich so implementiert, dass er selbständig den String vergrößert.
 
Zuletzt bearbeitet: (Rechtschreibung)
Ok, resize() kannst man noch machen in diesem Fall, aber ansonsten ist alles ok.
Hier nochmal update:

Code:
#include <string>
#include <iostream>
#include <fstream>

int main()
{
    std::ifstream in("input.txt", std::ios::in);
    std::string contents;
    contents.resize(10);
    in.read(&contents[0], 10);
    in.close();
    std::cout << contents << std::endl;
   
   return 0;
}

Testen könnt ihr das auf die schnelle hier, dass es funktioniert:
http://www.compileonline.com/compile_cpp11_online.php
 
Zurück
Oben