Mehrdimensionales Array aus Datei
-
Hallo,
Ich hab ein kleines Problem, ich mochte den Inhalt einer TxT in ein Mehrdimensionales Array und 2 Variablen einlesen. Der Inhalt meiner TxT sieht so aus:5 5 1,1,1,1,1, 1,2,2,2,1, 1,2,1,2,1, 1,2,2,2,1, 1,1,1,1,1;Die ersten zweil Zeilen sollten jeweils in eine Variable, das darunter in das Array. In der FoSu bin ich auf ein ähnliches Problem gestoßen, doch dort war es mit Vectoren gelöst. Könnt ihr mir vielleicht weiterhelfen?
Mfg,
Mick
-
Na dann zeig mal was du schon hast.
-
Was spricht denn gegen Vectoren?
Aber wenn es unbedingt mit rohen Arrays sein muss
. Folgendes Wissen brauchst du:
1. Zum Dateilesen: fstreams. Da es eigentlich keinen Unterschied zwischen Dateistreams und anderen iostreams wie cin und cout gibt, außer dass man die Datei vorher öffnen muss, sollte dies kein Problem sein.
1.1: Da du offensichtlich eine kommaseparierte Liste hast: Es gibt bei einigen Funktionen die Möglichkeit, Trennzeichen anzugeben. Damit sollte das Parsen einfacher sein.
1.2: Falls es noch konkrete Schwierigkeiten beim Parsen gibt: Nachfragen.
2. Für das Array: new und delete. Da die Dimensionen des Arrays erst feststehen wenn du die ersten beiden Zeilen der Datei eingelesen hast, kannst du die Arraygröße nicht fest in das Programm schreiben. Da helfen new und delete. Die Benutzung ist sehr einfach, dass was du hier brauchst dürfte in jeder Anleitung zu new und delete das erste oder zweite Beispiel sein.
3. Für den Lesealgorithmus: Schleifen natürlich und eventuell auch if-Abfragen, aber solche Grundlagen setze ich mal voraus.
3.1: Der Algorithmus zum Einlesen sollte keine Schwierigkeiten machen, wenn du die technischen Voraussetzungen aus den Punkten 1 und 2 erstmal kennst.
-
also mein Code sieht bis jetzt so aus:
ifstream input("map/testmap.txt"); Spielfeld = new int [mapX][mapY]; for(int j=0; j <= mapY; j++) { int i=0; for(i=0; i <= mapX; i++) { } }bis jetz noch ziemlich erbärmlich, aber weiter komm ich nicht und parsen hör ich jetz zum ersten mal. Es ist schon Ewigkeiten her, dass ich mal etwas eingelesen habe(und damals auch immernur Zeilenweise).
btw: ich möchte es nicht mit der Konsole machen. Alles was ich bis jetz gefunden hatte war mit Konsole (also cout...).edit:
Ich hab meine kleinen ;aps jetz so verändert, dass es nicht mehr mit Komma getrennt ist, damit dürfte das parsen doch wegfallen oder? Oder wird hier dann das " " als Trennzeichen gezählt?
-
Mach dich unbedingt mal über Ein-/Ausgabe mit Streams schlau. Das sind ganz wichtige Grundlagen. Ich zeige dir mal wie man das mit der Eingabe macht (ich gehe von der Version ohne Komma aus).
Das mit dem new und delete funktioniert so fast. Du kannst keine Arrays mehrdimensionalen Arrays anlegen, bei denen alle Dimensionen eine dynamische Größe haben. Die Details sind ziemlich technisch, ich erkläre das später. Der Punkt ist, dass man sich entweder dadurch behelfen muss, ein einzelnes großes Array anzulegen (d.h. in deinem Fall ein array der Größe mapX * mapY) und dann selber die Arrayindizes ausrechnet. Oder man reserviert das Array mittels new in mehreren Schritten, so wie ich das hier mal demonstriert habe, dann kann man den Indexoperator wie gewohnt benutzen. Oder man legt sich eine Klasse an, die das alles für einen erledigt, aber du hast ja anscheinend etwas gegen einfache Lösungen sonst würdest du std::vector nehmen.
Noch zwei wichtige Sachen:
- Denk daran alles was du mit new anforderst wieder mit delete freizugeben. Dabei für Arrays delete[] benutzen.
- Pass bei deinen Schleifen auf! Die Schleifen in deinem Ansatz zählen alle einen Index zu weit. Pass auf mit dem Schleifenanfang und der Abbruchbedingung! Sonst bastelst du dir ganz schnell ein paar schwer zu findende Fehler.Ok, hier ein Beispiel:
#include<fstream> #include<iostream> using namespace std; int main(){ ifstream input("map/testmap.txt"); // Header lesen int mapX, mapY; input >> mapX >> mapY; // dynamisches Array anlegen int **Spielfeld = new int*[mapY]; // Zuerst ein Array von int* anfordern for (int i=0; i<mapY;++i) Spielfeld[i]=new int[mapX]; // Und die int* dann auf dynamische eindimensionale Arrays zeigen lassen // Einlesen for(int i=0; i < mapY; ++i) { for(int j=0; j < mapX; ++j) // Faustregel: hinterster Index -> innerste Schleife { input >> Spielfeld[i][j]; } } // Ausgabe: Genau umgekehrt wie Eingabe for(int i=0; i < mapY; ++i) { for(int j=0; j < mapX; ++j) { cout << Spielfeld[i][j]; } cout << endl; } // WICHTIG! Speicher wieder freigeben for (int i=0; i<mapY;++i) delete[] Spielfeld[i]; delete[] Spielfeld; }Und warum kann man nun nicht einfach
new int[mapX][mapY]schreiben? Das erfährst du nach der nächsten Maus.
-
Maus
Arrays müssen immer Arrays von einem konkreten Datentyp sein. Dies ist notwendig, um beim Zugriff auf das Array die korrekte Adresse ausrechnen zu können. Wenn man nämlich auf
array[x]zugreift, wandelt der Compiler dies intern zu*(array + x)um, wobei array ein Zeiger auf das erste Element ist und die üblichen Regeln der Pointerarithmetik gelten. Für die korrekte Pointerarithmetik muss aber unbedingt bekannt sein, wie groß ein einzelnes Element des Arrays ist. Diese Größe kennt der Compiler aber nur bei konkreten Datentypen, wobei ein Array mit einer festen Anzahl an Elementen wiederum als ein Datentyp mit einer konkreten Größe gilt. Für zweidimensionale Arrays ist es daher notwendig, dass höchstens der erste Index dynamisch ist. Man hat dann ein dynamisches Array dessen Elemente vom Datentyp des Arrays mit der festen Elementzahl sind. Bei höherdimensionalen Arrays analog.
Also istnew int[X][3]ok. Es wird ein Array mit X Elementen vom Typ int[3] angelegt. Wenn auf das Array zugegriffen wird, weiß der Compiler wie groß die inneren Elemente sind und kann entsprechende Rechenschritte veranlassen. Abernew int[X][Y]geht nicht, weil int[Y] kein eigener Datentyp ist und der Compiler daher nicht weiß, wie er den Elementzugriff berechnen soll.
Die zwei üblichen Auswege sind:
a) Man benutztnew int[X*Y]und berechnet dann selber zur Laufzeit die korrekte Position der Elemente, etwa mittelsarray[x + y*x].
b) Anstatt ein Array von einem Array benutzt man Arrays von Zeigern. Denn Zeiger haben eine konkrete Größe, daher ist das kein Problem. Und die Zeiger lässt man dann auf dynamische Arrays mit Elementen eines konkreten Datentyps zeigen. Und da der Zugriff über Arrays und über Zeiger syntaktisch gleich ist, kann man wie gewohntarray[x][y]schreiben.
Nachteil der ersten Methode ist eine zusätzliche Rechnung die vorher nicht da war und die ungewohnte Syntax. Nachteil der zweiten Methode ist die zusätzliche Indirektion und die potentielle Speicherfragmentierung. Das sind keine gravierenden Nachteile und man kann sie auch mittels höherer C++ Sprachmittel (Klassen mit überladenen Operatoren, placement new) loswerden.Ich weiß nicht, ob dies eine didaktisch gute Erklärung ist - eher nicht. Ich argumentiere viel mit Zeigerarithmetik. Aber wenn man sich mit Zeigern auskennt, ist das ganze Problem ohnehin offensichtlich und man braucht keine Erklärung. Wenn man sich aber weigert die automatisierten Mittel der STL einzusetzen und stattdessen alles mit new und delete selber machen will, sollte man sich auch unbedingt mit Zeigern auskennen.
-
Vielen Dank!
Deine Beiträge hab mir wirklich sehr geholfen, ich versteh es jetz besser und mein Programm funktioniert.
Danke für die Mühe!Mfg,
Mick
-
Hallo,
wie ihr seht, hatte ich schon vor einiger zeit gefragt, wie man eine Datei in ein Array lädt (siehe erster Post).
Damals habe ich das Problem leicht anderst lösen können, doch nun muss ich es wirklich so machen, dass ich die mit Komma getrenten Werte in ein Array lade.Im moment sieht mein Code so aus:
char buffer; ifstream fin; fin.open("test.txt", ios::in); for(int i=0;i<pLenghtY;i++) for(int j=0;j<pLenghtX;j++){ fin.get(buffer); if(buffer!=',' && buffer!='\n'){ map[i][j] = atoi(&buffer); }else{j--;} } fin.close();zum einlesen funktioniert es auch, aber leider ist mir hinterher aufgefallen, dass in meiner Datei auch Werte vorkommen, die zwei oder mehr Stellen benötigen, also kann ich meine momentane Lösung leider vergessen.
Hier nochmal eine Beispiel-Datei, welche ich in das Mehrdimensionale Array einlesen möchte.
1,2,3,4,5,6,80,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, 31,32,33,34,35,36,37,38,39,1,41,42,43,44,45,46,47,48,49,50,1,52,53,54,55,56,57,58,59,60, 61,62,63,64,65,66,67,68,69,70,71,72,73,74,15,76,77,78,79,80,81,82,83,84,85,86,87,88,1,90, 91,92,93,94,95,96,97,98,99,100Wie kann ich das einfach lösen?
Schonmal vielen Dank!
Mfg,
Mick
-
Musst du das wirklich so machen? Andernfalls gäbe es noch die Möglichkeit,
dass du die Daten zum Beispiel als longs (mit 'write') in die Datei speicherst
und anschließend mit 'read' wieder ausliest. Da du hier angeben kannst wieviel
gelesen werden soll (nämlich in dem fall sizeof(long)) musst du dich dann
nicht mehr um Trennzeichen kümmern.
-
Die Datei kommt aus einem Tile-Map-Editor, den ich nicht selbst geschrieben habe (habe ich auch garkeine lust^^), also kann ich das leider nicht groß beeinflussen.
EDIT:
Ich habe jetz endlich nach langer Zeit suche eine weitere möglichkeit entdeckt:ifstream fin; fin.open("test.txt", ifstream::in); int buffer; for(int i=0;i<pLenghtY;i++) for(int j=0;j<pLenghtX;j++){ fin >> buffer; fin.ignore(256, ','); map[i][j]=buffer; }Vielleicht hilft es ja jemandem.
-
Benutze einfach den >> operator:
ifstream fin(...); for(x=0; x < ...; ++x) for(y=0; y < ...; ++y) { char dummy; fin >> map[x][y]; // einen int - Wert einlesen fin >> dummy; //ein Komma einlesen }