3 Stimmen

Schnelle und effiziente Methode zum Einlesen einer durch Leerzeichen getrennten Zahlendatei in ein Array?

Ich brauche eine schnelle und effiziente Methode, um eine durch Leerzeichen getrennte Datei mit Zahlen in ein Array zu lesen. Die Dateien sind auf diese Weise formatiert:

4 6
1 2 3 4 5 6
2 5 4 3 21111 101
3 5 6234 1 2 3
4 2 33434 4 5 6

Die erste Zeile ist die Dimension des Arrays [Zeilen Spalten]. Die folgenden Zeilen enthalten die Daten des Arrays.

Die Daten können auch ohne Zeilenumbrüche wie folgt formatiert werden:

4 6
1 2 3 4 5 6 2 5 4 3 21111 101 3 5 6234 1 2 3 4 2 33434 4 5 6

Ich kann die erste Zeile lesen und ein Array mit den Zeilen- und Spaltenwerten initialisieren. Dann muss ich das Array mit den Datenwerten füllen. Meine erste Idee war, die Datei Zeile für Zeile zu lesen und die Split-Funktion zu verwenden. Aber das zweite aufgeführte Format lässt mich zögern, da die gesamten Array-Daten auf einmal in den Speicher geladen werden würden. Einige dieser Dateien sind mehrere 100 MB groß. Die zweite Methode wäre, die Datei in Stücken zu lesen und sie dann Stück für Stück zu analysieren. Vielleicht hat jemand anderes eine bessere Methode dafür?

2voto

Eric J. Punkte 143512

Wie nutzen Sie die Daten, sobald sie geladen sind? Müssen Sie in der Regel jedes Array-Element berühren oder werden Sie nur spärliche/zufällige Zugriffe vornehmen?

Wenn Sie die meisten Array-Elemente berühren müssen, ist das Laden in den Speicher wahrscheinlich die beste Lösung.

Wenn Sie nur auf bestimmte Elemente zugreifen müssen, sollten Sie die benötigten Elemente in den Speicher laden (Lazy Load). Eine Strategie bestünde darin, festzustellen, welches der beiden Layouts die Datei verwendet (mit/ohne Zeilenumbruch), und einen Algorithmus zu entwickeln, der ein bestimmtes Element bei Bedarf direkt von der Festplatte lädt (Suche nach dem angegebenen Dateiversatz, Lesen und Parsen). Für einen effizienten erneuten Zugriff auf dasselbe Element könnte es sinnvoll sein, das einmal gelesene Element in einem durch den Offset indizierten Wörterbuch zu speichern. Das Wörterbuch sollte zuerst überprüft werden, bevor die Datei nach einem bestimmten Wert durchsucht wird.

Grundsätzlich würde ich den einfachen Weg wählen, es sei denn, Ihre Tests beweisen, dass Sie einen komplizierteren Weg gehen müssen ( eine vorzeitige Optimierung zu vermeiden ).

2voto

TreDubZedd Punkte 2493

Lesen Sie die Datei zeichenweise ein. Wenn es sich um Leerzeichen handelt, beginnen Sie eine neue Nummer. Wenn es eine Ziffer ist, verwende sie.

für Zahlen mit mehreren Ziffern eine Zählervariable behalten:

int counter = 0;
while (fileOpen) {
    char ch = readChar(); // use your imagination to define this method.
    if (isDigit(ch)) {
        counter *= 10;
        counter += asciiToDecimal(ch);
    } else if (isWhitespace(ch)) {
        appendToArray(counter);
        counter = 0;
    } else {
        // Error?
    }
}

Zur Klarstellung editiert.

1voto

Marc Gravell Punkte 970173

Wie wäre es damit:

    static void Main()
    {
        // sample data
        File.WriteAllText("my.data", @"4 6
1 2 3 4 5 6
2 5 4 3 21111 101
3 5 6234 1 2 3
4 2 33434 4 5 6");

        using (Stream s = new BufferedStream(File.OpenRead("my.data")))
        {
            int rows = ReadInt32(s), cols = ReadInt32(s);
            int[,] arr = new int[rows, cols];
            for(int y = 0 ; y < rows ; y++)
                for (int x = 0; x < cols; x++)
                {
                    arr[y, x] = ReadInt32(s);
                }
        }
    }

    private static int ReadInt32(Stream s)
    { // edited to improve handling of multiple spaces etc
        int b;
        // skip any preceeding
        while ((b = s.ReadByte()) >= 0 && (b < '0' || b > '9')) {  }
        if (b < 0) throw new EndOfStreamException();

        int result = b - '0';
        while ((b = s.ReadByte()) >= '0' && b <= '9')
        {
            result = result * 10 + (b - '0');
        }
        return result;
    }

Eigentlich ist dies nicht sehr spezifisch über die Begrenzer - es wird so ziemlich davon ausgehen, dass alles, was nicht eine ganze Zahl ist ein Begrenzer ist, und es unterstützt nur ASCII (Sie verwenden ein Lesegerät, wenn Sie andere Kodierungen benötigen).

0voto

Lester Punkte 4093

Sofern der Rechner, auf dem Sie diese Textdateien analysieren, nicht eingeschränkt ist, sollten Dateien von einigen hundert MB noch in den Speicher passen. Ich würde vorschlagen, mit Ihrem ersten Ansatz des Lesens von Zeile zu Zeile und mit Split zu gehen.

Wenn der Speicherplatz ein Problem wird, sollte Ihr zweiter Ansatz, das Lesen in Abschnitten, gut funktionieren.

Was ich damit sagen will, ist, dass man es einfach einführen und messen sollte, ob die Leistung ein Problem darstellt.

0voto

Rubys Punkte 3147

Gehen wir davon aus, dass wir die gesamte Datei in eine Zeichenkette eingelesen haben.
Sie sagen, dass die ersten beiden Zeilen und Spalten sind, also müssen wir die Zahlen auf jeden Fall analysieren.
Danach können wir die ersten beiden nehmen, unsere Datenstruktur erstellen und sie entsprechend füllen.

var fileData = File.ReadAllText(...).Split(' ');
var convertedToNumbers = fileData.Select(entry => int.Parse(entry));
int rows = convertedToNumbers.First();
int columns = convertedToNumbers.Skip(1).First();
// Now we have the number of rows, number of columns, and the data.
int[,] resultData = new int[rows, columns];
// Skipping over rows and columns values.
var indexableData = convertedToNumbers.Skip(2).ToList();
for(int i=0; i<rows; i++)
    for(int j=0; j<columns; j++)
        resultData[i, j] = inedexableData[i*rows + j];

Eine Alternative wäre, die ersten beiden aus einem Stream zu lesen, das Array zu initialisieren und dann n Werte auf einmal zu lesen, was kompliziert wäre. Außerdem ist es am besten, Dateien so lange wie möglich offen zu halten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X