PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Matrix aus Datei einlesen



BertanARG
13-07-2007, 10:57
Hi,

ich habe Matrizen in .dat-Dateien gespeichert. Diese haben zwischen den Werten in jeder Zeile ein ";" als Trennzeichen (Nach dem letzten Zeileneintrag steht kein ";" mehr!)

Nun möchte ich diese Matrizen mit einem Programm in ein Array P[n][n] einlesenn.

Ich habe online einige Beispiele gefunden, die jedoch nicht funktionierten. fstream und ähnliche Befehle.

Ich wäre für eure Hilfe wirklich dankbar

SebastianKN
13-07-2007, 12:46
vielleicht hilft Dir die Funktion strtok weiter (aus string.h).
könnte dann so ähnlich aussehn:



int spalte = 0;
char zeile[] = "1;2;3"; // eingelesene Zeile aus Datei
char* p;
p = strtok(zeile, ";");
while( p ) {
// Inhalt von p in richtiges Feld schreiben
// (Spalte mit variable spalte ermitteln)
// (vorher in int umwandeln)

spalte++;
p = strtok(NULL, ";");
}

Wenn Du Dich nicht um die Größe Deines 2D-Arrays kümmern möchtest, ist vielleicht ne andere Datenstruktur geeigneter. Vielleicht geht das ja mit vector (falls Du c++ verwendest). Die kann man bestimmt in einander verschachteln und dann auch per Index drauf zugreifen. Dann brauchst Du auch die variable spalte nicht.
Wissen die anderen aber vielleicht besser ;)

BertanARG
13-07-2007, 17:04
Hi,

ich konnte es folgendermaßen lösen...

ifstream IN ( "UMatrix.dat" , ios::in );
k=1;
while(k<=n)
{
for(int l=1;l<=n;l++)
{
IN >> SV[k][l];
if(l==n)
{
k++;
}
}
}

Allerdings geht das nur, wenn die Matrixeinträge ohne Semikolon getrennt. Das Semikolon verhindert hier das richtige Einlesen.
Da das Erstellen der Matrizen sehr aufwendig war, kann ich das nicht ändern. Auch Ersetzen geht nicht, die Matrizen haben um die 5000x5000 Einträge.

Wie kann ich es hinkriegen, dass nach ";" getrennt werden soll?

SebastianKN
13-07-2007, 17:51
mhh, keine Ahnung. Musst vielleicht irgendwie den operator>> ändern/überladen... Vielleicht kann man das Trennzeichen auch irgendwo einstellen.

Warum ersetzt Du nicht einfach die Semikolons durch Leerzeichen? Geht doch im Editor recht schnell. Wenns viele Dateien sind und Du unter Linux unterwegs bist, hilft vielleicht folgendes Kommando:


find . -type f -name '*.dat' -exec perl -i.~ -nae 's/;/ /g;print' {} \;

Das Kommando führst Du in dem Verzeichnis aus, in dem die Dateien liegen. Für jede geänderte Datei wird dann ne Sicherungskopie erstellt.


kleine Bitte: Rück Deinen Code mit Leerzeichen ein und verwende die Code-Tags...

BertanARG
13-07-2007, 18:10
Hi,

ne, bin leider nicht unter Linux unterwegs. Und wenn ich in Windows versuche die Zeichen zu ersetzen, dauerts ewig. Das für 72 dateien zu machen ist zeitlich nicht drin.

Ich glaube, mit "fscanf" könnte es gelingen. Allerdings kriege ich den Befehl nicht zum Laufen.

die matrix sieht in etwa so aus 0;0;0.2334;0.234;0;...
daher habe ich den Befehl

fscanf("Datei.dat","%f%c",&x[k],&abc);

verwendet. Alllerdings ohne Erfolg. Kann mir jemand ein Miniprogramm schreiben, in dem fscanf funktioniert, damit ich mal sehen kann was der Befehl genau macht?

tschloss
13-07-2007, 19:03
Hi,

ne, bin leider nicht unter Linux unterwegs. Und wenn ich in Windows versuche die Zeichen zu ersetzen, dauerts ewig. Das für 72 dateien zu machen ist zeitlich nicht drin.

Ich glaube, mit "fscanf" könnte es gelingen. Allerdings kriege ich den Befehl nicht zum Laufen.

die matrix sieht in etwa so aus 0;0;0.2334;0.234;0;...
daher habe ich den Befehl

fscanf("Datei.dat","%f%c",&x[k],&abc);

verwendet. Alllerdings ohne Erfolg. Kann mir jemand ein Miniprogramm schreiben, in dem fscanf funktioniert, damit ich mal sehen kann was der Befehl genau macht?

Also der erste Paramter ist doch ein File-Handle, keine Filename. Weiter habe ich nicht gelesen....

http://www.cplusplus.com/reference/clibrary/cstdio/fscanf.html

Hast du dich schon irgendwie mit der Materie befasst?

tribad
13-07-2007, 20:14
Es gibt viele Wege, die zum Ziel führen.

strtok() ist nur interessant, wenn man mehrere delimiter haben kann. Es verwendet, soweit mir bekannt, einen internen buffer um die Aufbereitung hinzubekommen.

Ich würde strchr() nehmen.


schau dir das einfach mal an.

Es wird auf einem char-buffer aList gearbeitet. Den kann man leicht mit fgets einlesen. Da ich hier vorher nicht weiß wieviele elemente in der Liste sind, zähle ich sie vorher aus und erzeuge mir dann ein passendes array.


s=strchr(aList, ',');
while (s != 0) {
count ++;
s = strchr(s+1, ',');
}
//
// create the list. First alloc memory.
plist=new int[count];
if (plist != 0) {
memset(plist, 0, sizeof(int)*count);
} else {
return;
}

Und dann konvertiert man hier die werte in das array. dabei wird es ausgenutzt, das atoi nur bis zum ersten non-digit konvertiert. Man muß also das komma nicht ersetzen.



i=0;
s=aList;
do {
plist[i]=atoi(s);
s=strchr(s+1, ',');
if (s != 0) {
i++;
s++;
} else {
}
} while (s != 0);

BertanARG
13-07-2007, 22:40
Hi,

ich versuche den Syntax mal zu übernehmen. Allerdings fange ich noch nicht so viel damit an.
C++ verwende ich eigentlich eher selten, aufgrund des Umfangs meiner Diplomarbeit musste ich es verwenden um Auswertungen auf parallelen Rechner durchführen zu lassen.

Ansonsten verwende ich lieber VBA, da ich es logischer und übersichtlicher finde. Das ist sicher eine Geschmacksfrage, und hat je nach Bedarf Vor- und Nachteile.

Was ist denn ein File-Handle? Ist fscanf also prinzipiell nicht geeignet? Warum gibt es keinen Befehl
getdouble(filename,";") für die Herkunft, den Typ und das Trennzeichen. Mehr wäre doch im Prinzip nicht nötig um die Daten zu lesen.

Mit den beiden Quellcodes fange ich leider nichts an.


Noch mal kurz...

ich habe ein File der Form

0.123;0.284;0.8734;0;0;0.23;0.7524
0.4123;0.6284;0.81734;0;0.32;0;0.7524
0.1223;0.3284;0.48734;0.234;0;0.875;0.7524
.
.
.
Die Anzahl n ist bekannt... Es gibt n Einträge in n Zeilen.

Alles was ich benötige ist ein Befehl, um die Zahlenwerte in ein nxn-Array zu lesen.
Aber ich finde absolut nichts, womit das funktioniert. Mit einem Instream funktioniert es, solange kein ";" als Trennzeichen verwendet wird. Und aufgrund der Größe n>5000 funktioniert das Ersetzen (statt ; ein Leerzeichen) in der Datei selbst auch nicht, und es wäre unpraktisch.

Das kann doch kein so großes Problem für C sein.


Grüße,
BertanARG

quinte17
14-07-2007, 11:36
bei 7 werten ein nxn zu lesen stell ich mir schwierig vor...

du könntest ja einfach mal in code-tags deinen jetzigen code in c++ posten, bei dem es ohne ";" geht. vielleicht kann man dann schnell erkennen warum dies nicht geht. es gibt ja hier immerhin viele fähige leute..

greetz

BLUESCREEN3D
14-07-2007, 12:50
ich konnte es folgendermaßen lösen...
(...)

Warum gibt es keinen Befehl
getdouble(filename,";")
Mit dem Programm aus dem ersten Post hast du es schon fast. Und so einen ähnlichen Befehl gibt es in C++ auch. Das ganze könnte z.B. so aussehen:

#include <sstream>

(...)

double matrix[n][n];

for (unsigned int row=0; row<n; row++)
{
for (unsigned int col=0; col<n; col++)
{
//read:
stringstream s;
cin.get(*s.rdbuf(), ((col==n-1)?'\n':';'));
cin.get(); //discard delimiter

//convert:
s >> matrix[row][col];

//display:
cout << "row " << row << ", col " << col << ": ";
cout << matrix[row][col] << endl;
}
}
Du musst natürlich cin durch deine ifstream-Instanz ersetzen. Oder du benutzt cat dateiname | ./programmname.

Ich gehe davon aus, dass die Textdateien Unix-Zeilenwechsel, also nur ein einfaches \n enthalten. Wenn nicht: dos2unix.

edit: Ich sehe gerade, dass du nicht unter Linux bist. Das mit den Zeilenwechsel ist aber egal, da unter Windows \r\n verwendet wird und das \r bei der Konvertierung zu double ignoriert wird.

BertanARG
14-07-2007, 16:12
Hi,

tausend Dank.. Es funktioniert. Ich musste noch nicht einmal das cin durch das IN ersetzen.
Allerdings würde ich jetzt auch gerne verstehen, wie es funktioniert.

Diese drei Zeilen sorgen ja für das Einlesen...


stringstream s;
cin.get(*s.rdbuf(), ((col==n-1)?'\n':';'));
cin.get(); //discard delimiter


1. Was für eine Variable ist denn der stringstream? In welcher Form werden dort
die Matrixeinträge hinterlegt?
2. Dann würde ich gerne wissen, was beim cin.get(...) geschieht. Kann ich sogar dafür sorgen, dass die letzte Spalte nicht mehr gelesen wird, wenn ich statt n-1 n-2 wählen würde? Für eine Erklärung der get-Parameter wäre ich dankbar.
3. Aus welchem Grund steht das cin.get() noch einmal dort? Weswegen ist diese Befehlszeile noch von Bedeutung?


Danke nochmal,
viele Grüße,
BertanARG

BertanARG
14-07-2007, 16:20
Nochmal hallo,

da hatte ich mich zu früh gefreut. Es funktioniert leider doch nicht... Hier der gesamte Quellcode...


#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main()
{
int n=105;
int j=1;
double x[106][106];

ifstream IN ( "UMatrix.dat" , ios::in );
while(i<=n)
{
for(int j=1;j<=n;j++)
{
IN >> x[i][j];
if(j==n)
{
i++;
}
}
}

for (unsigned int row=0; row<n; row++)
{
for (unsigned int col=0; col<n; col++)
{
//read:
stringstream s;
IN.get(*s.rdbuf(), ((col==n-1)?'\n':';'));
IN.get(); //discard delimiter

//convert:
s >> x[row][col];

//display:
cout << "row " << row << ", col " << col << ": ";
cout << x[row][col] << endl;
}
}


ofstream OUT ( "Kopie3.dat" );
for(int r=1;r<=n;r++)
{
for(int s=1;s<=n;s++)
{
OUT<<x[r][s]<<" ";
}
OUT<<"\n";
}
//SCHLIESSE DEN OUTPUT STREAM
OUT.close();

system("PAUSE");
return 0;
}


Es funktioniert leider nicht, ich erhalte einen ähnlichen Fehler wie beim Einlesen mit IN.
Die Matrix besteht aus lauter Nullen, und lediglich am Ende erscheinen unsinnige Werte.

So langsam bin ich echt am Verzweifeln, das kann doch nicht sooo schwer sein.

BertanARG
14-07-2007, 16:32
es heißt natürlich i=1, nicht j=1... hatte das kurzfristig geändert.

BertanARG
14-07-2007, 17:12
Nochmal hallo,

Naja, im Augenblick probiere ich wieder den fscanf-Syntax. Immerhin habe ich ihn mal halbwegs zum Laufen gebracht. Allerdings kann ich mit fopen die Datei nicht richtig öffnen...
Hier der Code



int main(int argc, char **argv)
{
int j,k,l,m,r,s;
double x[40][40];
char abc;
int test;
FILE *fp;

fp=fopen("Test.txt","r");
test=fgetc(fp);
cout<<test<<endl;

for(k=1;k<=1;k++)
{
for(l=1;l<=3;l++)
{
fscanf (fp, "%f%c",&x[k][l],&abc);
cout<<x[k][l]<<endl;
}
}
cout<<abc<<endl;
system("PAUSE");
return 0;
}


Für den Character ";" verwende ich einmal %c oder %d, bin mir nicht sicher, welcher der richtige ist. Funktionieren tut aber keine Version.

Die Test-Datei hat nur einen Wert, nämlich 1234. Wenn ich die Integervariable "test" ausgeben lasse, erhalte ich allerdings den Wert 49.
Diesen Wert erhalte ich ständig, wenn ich eine Zuweisung durchführe.
Weise ich abc=fgetc(fp); zu, wird glücklicherweise 1 ausgegeben.

Gibt es eigentlich eine Stringvariable in C++? Wenn ich ein Characterfeld abc[40] anlege, funktioniert die Zuweisung nicht. Ich muss doch nicht etwa jedem Charakterfeld separat einen Buchstaben zuordnen, oder etwa doch?

wie kriege ich es nun hin, dass ich Zahlenwerte aus dem File ablesen kann? Und als nächstes würde ich gern wissen, wie man dann Einträge der Form
23;434;23;2433; ablesen kann.


Danke schon mal

BertanARG
14-07-2007, 17:32
Hi,

fscanf läuft, allerdings habe ich den Eindruck dass dieser Befehl nicht für meine Bedürfnisse geeignet ist.
Ich habe ihn mit Chars, Integers und Strings getestet und habe den Eindruck gewonnen, dass er den kompletten Text erfasst und man unmittelbar allen Objekten einen Variablentyp zuordnen muss.

Das heißt mein Befehl müsste
fscan(fp,%f;%f;...;%f\n;%f;...,&x[1][1],&x[1][2],...,&x[n][n]) aussehen.
Bei einer 5000x5000-Matrix absolut inakzeptabel.

Ich bin verzweifelt, habe ich das schon erwähnt? Kann mir keiner helfen?

BLUESCREEN3D
14-07-2007, 19:29
1. Was für eine Variable ist denn der stringstream? In welcher Form werden dort
die Matrixeinträge hinterlegt?
stringstream ist eine Klasse und ist sowas wie ifstream nur das halt nicht aus/in Dateien gelesen/geschrieben wird, sondern in Strings.


2. Dann würde ich gerne wissen, was beim cin.get(...) geschieht. Kann ich sogar dafür sorgen, dass die letzte Spalte nicht mehr gelesen wird, wenn ich statt n-1 n-2 wählen würde? Für eine Erklärung der get-Parameter wäre ich dankbar.
3. Aus welchem Grund steht das cin.get() noch einmal dort? Weswegen ist diese Befehlszeile noch von Bedeutung?
Die erste Zeile cin.get(*s.rdbuf(), ((col==n-1)?'\n':';')); macht folgendes:
Der erste Parameter ist eine Instanz von stringbuf (noch eine Klasse). Dieser stringbuf ist in dem stringstream drin und man braucht den Aufruf von rdbuf(), weil das get() leider nicht direkt in den stringstream schreiben kann.
Der zweite Parameter ist meistens ein Semikolon und in der letzten Spalte ein \n und gibt an, dass bis direkt vor diesem Zeichen gelesen werden soll.
Insgesamt bedeutet die Zeile: Von cin soll gelesen werden und zwar bis vor das Trennzeichen und das Ergebnis soll in s gespeichert werden.

Da das Trennzeichen nicht mitgelesen wird und wir beim nächsten Aufruf die nächste Zahl lesen wollen und nicht das Trennzeichen davor, muss mit cin.get() ein Zeichen, nämlich genau das Trennzeichen, "weggelesen" werden.


da hatte ich mich zu früh gefreut. Es funktioniert leider doch nicht... Hier der gesamte Quellcode...
Wenn du den alten Code drin lässt, der schon aus der Datei liest, kann es ja nicht klappen. Schmeiss mal oben die while-Schleife mitsamt ihrem Inhalt raus.

Wenn es dann immernoch nicht klappt, kannst du so einen Überblick kriegen:
Hinter "display" diese Zeile einfügen:

cout << "Ein Wert gelesen: " << s.str() << endl;

BertanARG
14-07-2007, 20:18
Hi,

okay, jetzt funktioniert es. Ich danke dir wirklich vielmals. Aber ich verstehe nicht, weswegen es nicht funktionieren konnte solange die while-schleife noch darin war. Kannst du mir das noch erklären?

Und zu guter letzt habe ich noch eine Frage zum Einlesen und Ausgeben spezieller Dateinamen.

Die Datei heißt im eigentlich in etwa "a1_a2_a3_UMatrix.dat".
In meiner Auswertung lasse ich darauf neue Dateien ausgeben, die ich später zum Visualisieren benötige.

Hierfür wäre es gut, wenn ich daraufhin meine ausgegeben neuen Dateien z.B. so nennen könnte...
"a1_a2_a3_Ergebnis.dat".

Gibt es eine Möglichkeit diesen Teil des Dateinamens auszulesen, in einem String zu speichern und später den Ausgabe-dateien im Namen anzufügen?

Die Ausgabe von Parametern im Dateinamen ist mir bereits bekannt, entscheidend ist dass aus dem alten Dateinamen ein Teil entnommen wird. Wie das geht, weiß ich nämlich nicht.


Grüße,
und danke schon mal.

BLUESCREEN3D
15-07-2007, 20:14
string a="a1_a2_a3_UMatrix.dat";

stringstream b;
b << a.substr(0,a.rfind('_')) << "_Ergebnis.dat";

cout << b.str() << endl;

panzi
23-07-2007, 01:21
Ok, das ist jetzt nicht sehr produktiv weil nicht c/c++ aber in python geht's wie folgt (Aber eine std::readline Funktion und sowas Ähnliches wie split gibt's doch auch in c++, wenn ich mich nicht irre?):


matrix = [[int(val) for val in row.split(";")] for row in open("matrix.dat")]
Oder wenn man Tupel lieber als Listen hat kann man's so machen:

matrix = tuple(tuple(int(val) for val in row.split(";")) for row in open("matrix.dat"))

Wobei aber Leerzeilen hier auch als Zeilen der Matrix (mit 0 Elementen) erkannt werden. D.h. weiters das die einzelnen Zeilen unterschiedliche Länge haben können.