Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Einlesen großer CSV Dateien
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

Einlesen großer CSV Dateien

beantworten | zitieren | melden

verwendetes Datenbanksystem: <CSV>

Hallo,
ich scheitere beim Einlesen großer CSV Dateien in eine DataTable an einer OutOfMemoryException (Methode getDataTableFromCSV1).

Das Anlegen einer wesentlich größeren DataTable funktioniert aber (Methode generateDataTable). Die Strings in dem CSV sind übrigens deutlich kleiner als der test-string.

Ich habe schon allerhand gelesen, dass die String.Split()-Methode das Problem sein könnte, aber ich weiß nicht, wie ich diese umgehen könnte ...

Ich habe auch schon den LUMEN CSV Parser verwendet - auch hier ein OutOfMemory.

Bevor jemand fragt: Ja, ich brauche alle Daten (um Statistiken zu berechnen - ANOVAs etc).

Danke und Gruß,

Jan

 

using System.IO;
using System.Text;
using System.Data;

namespace CSVImport
{
    public class Class1
    {

        static void Main(string[] args)
        {

           
            // works 15000 columns x 15000 rows, process takes around 1.2gb
            generateDataTable();

            // out of memory, only 9000 columns x 12000 rows, process > 1.8gb
            getDataTableFromCSV("C:\test.csv", bool verbose)

         }

    }

   private static void generateDataTable()
        {

            TimeSpan tStart = (DateTime.UtcNow - new DateTime(1970, 1, 1));

            DataTable dt = new DataTable(); 
            for (int i = 0; i < 15000; i++)
            {
                dt.Columns.Add(new DataColumn("column "+i.ToString(),typeof(string)));
            }

            for (int j=0; j<15000; j++){
                
                DataRow r = dt.NewRow();
                for (int i = 0; i < 15000; i++)
                {

                    r[i] = "this is a relatively long string to test memory for a datatable";
   
                }
                dt.Rows.Add(r);
            }

            TimeSpan tNow = (DateTime.UtcNow - new DateTime(1970, 1, 1));
            if (true)
            {
                Console.WriteLine(tNow.TotalSeconds - tStart.TotalSeconds);
            }


            Console.WriteLine("Columns: " + dt.Columns.Count.ToString());
            Console.WriteLine("Rows: " + dt.Rows.Count.ToString());


        }

       public static DataTable getDataTableFromCSV(string path, bool verbose){

            DataTable dt = new DataTable(path);
            TimeSpan tStart = (DateTime.UtcNow - new DateTime(1970, 1, 1));

            try
            {
                using (StreamReader readFile = new StreamReader(@path))
                {
                    string line;
                    string[] row;
                    bool isFirstLine = true;

                    while ((line = readFile.ReadLine()) != null)
                    {
                        row = line.Split(',');
                        if (isFirstLine)
                        {
                            foreach (string column in row)
                            {
                                dt.Columns.Add(column);
                              }
                          isFirstLine = false;
                        }
                        else
                        {

                            DataRow r = dt.NewRow();
                            for (int i = 0; i < row.Length; i++)
                            {
                                r[i] = row[i];
                            }
                            
                           dt.Rows.Add(r);
                           
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            TimeSpan tNow = (DateTime.UtcNow - new DateTime(1970, 1, 1));
            if (verbose)
            {
                Console.WriteLine(tNow.TotalSeconds - tStart.TotalSeconds);
            }

            return dt;

        }
}

 
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5987
Herkunft: Leipzig

beantworten | zitieren | melden

Hi janDD,

was genau sind bei dir "große" Dateien? Wenn die Datenmenge größer ist als der verfügbare Speicher, kannst du halt nichts machen. In dem Fall würde ich dir raten, die Daten zuerst in eine Datenbank zu importieren, und sie dort weiterzuverarbeiten.

Christian
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Eine Split Funktion über 9000 columns liefert ein großes Array zurück (wobei die Größe im Auge des Betrachters liegt). Auch hier kann man Speicherplatz sparen. Statt ein Array könnte man hier eine Schleife durchlaufen lassen, welche immer nur den nächsten Feldinhalt verarbeitet.

Pseudocode:
..für jedes Zeichen
..{
....Sammel Zeichen bis Trennzeichen oder Ende als Feldinhalt
....Wenn Feldinhalt vollständig gesammelt:
....{
......verarbeite Feldinhalt
....}
..}

Gruß, CoLo
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

Hi,

die Größe der Datei ist 1gb. Die Strings sind aber kürzer als mein Teststring "this is a relatively long string to test memory for a datatable" und die CSV-Datei hat weniger Spalten/Zeilen als meine künstlich erstellte DataTable -- daran kann's also nicht liegen.

Grüße,
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Evtl. stürzt er ja schon bei ReadLine ab.
Wo stürzt er denn ab, kannst Du das ermitteln?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von CoLo am .
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

Ja, er stürzt beim ReadLine ab ... aber das ist m.E. Zufall --- es ist halt die erste Funktion, die nicht mehr genug Speicher bekommt...

Ich hab es auch mal Deinen Vorschlag verwendet, den String zeichenweise zu durchlaufen ... gleiches Problem

Grüße,
Jan
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Probier mal, ob er beim ReadLine abstürzt, wenn Du quasi alles nach Readline auskommentierst.

Gruß, CoLo
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10083

beantworten | zitieren | melden

Natürlich stürzt die Anwendung mit OutOfMemory ab.

1GB Datei bedeutet schon mal bei einlesen nach UniCode ( intern ist jeder sting Unicode also 16Bit) das mindestens 2GB im Speicher stehen würden.
Dann noch die Verwaltungsinfos der DataTable und schon bist du weit über dem was man benutzen kann.

Warum meinst du so eine riesige Datei komplett einlesen zu müssen?
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

Nein, es ist eben nicht "natürlich", dass er abstürzt. Meine Methode 1 läuft problemlos.
MaW, ich kann problemlos eine DataTable mit 15000 Spalten und 15000 Zeilen anlegen, die den teststring "this is a relatively long string to test memory for a datatable" enthalten.

Die Kombination aus einlesen + datatable anlegen ist der knackpunkt. Aber mir ist nicht klar wo, denn schließlich liest der Streamreader ja immer nur eine Zeile. Scheinbar räumt der Garbage Collector die Zeilen nicht vernünftig wieder weg oder der Streamreader ist buggy ... k.a.

Und ja ... ich brauche die große Datei (um verschiedene Statistiken zu berechnen, Stichwort ANOVA)

@CoLo: wenn ich alles auskommentiere, läuft es durch. Es läuft sogar durch, wenn ich statt


r[i] = row[i];
folgendes schreibe


r[i] = "this is a relatively long string to test memory for a datatable";

Das ist mir völlig uklar. Denn auch hier rattert der Streamreader über alle Zeilen...
Mhmm..

Grüße,
Jan
private Nachricht | Beiträge des Benutzers
weismat
myCSharp.de - Member



Dabei seit:
Beiträge: 878
Herkunft: Frankfurt am Main

beantworten | zitieren | melden

Der Optimizer der CLR wird die String Konstante ueberall als gleichen Zeiger auf den festen Wert in ein Array einsetzten und nicht blind in den Speicher schreiben.
Kannst ja mal Zufalls-Strings mit einer festen Laenge testen...
FZelle hat schon recht - Dir wird nur eventuell 64 Bit und ein grosser freier Speicher >4 GB helfen...
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von weismat am .
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10083

beantworten | zitieren | melden

@janDD:
Da leigst du aber falsch.
Strings in .NET sind Immutable, aber da Du ja immer den selben ( nicht den gleichen ) String verwendest hast du danach nicht 22.500.000 Strings im Speicher.
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Du brauchst alle Daten. Die Frage ist, brauchst Du sie alle sofort.

Evtl reicht Dir ja auch, wenn Du alle Daten scheibchenweise wegschreibst und scheibchenweise wiedereinliest (MrSparkles Idee)?. So dass Du als Ziel immer nur einen Datensatz statt alle im Speicher hast.
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

Hm, verstehe.
die datatable übergebe ich normalerweise bestimmten funktionen, die die berechnungen anstellen. das zeilenweise zu machen, wäre ein riesiger aufwand ...

gibt es denn vllt. eine möglichkeit, die datatable auf platte zu swappen, sobald sie aus dem ram läuft?
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5987
Herkunft: Leipzig

beantworten | zitieren | melden

Zitat von MrSparkle
In dem Fall würde ich dir raten, die Daten zuerst in eine Datenbank zu importieren, und sie dort weiterzuverarbeiten.
Christian
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

eine datenbank, die tabellen mit mehr als 10000 spalten erlaubt, ist mir gar nicht bekannt ...
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Kannst ja 10000 Zeilen daraus machen xD. Was beinhalten die Spalten denn?
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

Prozessdaten...
Ja sicher kann ich 10000 Zeilen draus machen ... und aus den 12000 Zeilen dann 12000 Spalten?
Ja, ich könnte die Tabellen auch normalisieren ... aber um meine Analyse zu machen, muss ich sie wieder zusammenführen ...

Es gibt in C# ja sowas wie memory mapped files ... kann ich sowas vllt. für meine DataTable nutzen ... ?
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5987
Herkunft: Leipzig

beantworten | zitieren | melden

Es gibt auch die BufferedStream-Klasse für solche Zwecke. Aber das nützt dir ja nix, weil du ja wohl trotzdem sämtliche Daten im Speicher brauchst, um damit zu arbeiten.
Eine Datenbank zu verwenden war mein Vorschlag, weil eine Datenbank genau für solche Zwecke entwickelt wurde. Wie du deine Daten in die DB reinbekommst, ist dein Problem. Du könntest die Spalten auf mehrere Tabellen aufteilen oder ein gescheites DB-Modell daraus entwickeln.
Christian
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Sei kreativ =). Angenommen Du hast 1 Tabelle mit 2 Datensätze mit Inhalt:

tabSatz:
1a,1b,1c
2a,2b,2c


Dann könntest Du z.B. daraus 2 Tabellen machen:

tabSatz:
ID1
ID2

tabSatzdaten:
ID1, 1a
ID1, 1b
ID1, 1c
ID2, 2a
ID2, 2b
ID2, 2c
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

or leute ... ich hab schon mehrere schemata designed in allen möglichen normalformen.
mein problem ist, dass ich sehr sehr umfangreiche berechnungen mit der datatable anstelle und dafür sind schon alle methoden fertig (>20000 Zeilen code!) und mit kleineren files in benutzung, getestet usw. usf.
die möchte ich gern verwenden, ohne ein halbes jahr mich mit irgendwelchen neuen sql schnipseln rumzuärgern.

daher würde ich gern irgendwie (und wenn es noch so absurd ist) meine daten in eine datatable haben ...

ist das sooooo unverständlich?


aber vielleicht gibt es ja wirklich keinen weg. aber bitte nicht mehr eine datenbank vorschlagen ...

jan
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden


r[i] = double.Parse(row[i]);
//oder
r[i] = readFile.Position + ";" + i + ";" + row[i].Length;
private Nachricht | Beiträge des Benutzers
MrSparkle
myCSharp.de - Team

Avatar #avatar-2159.gif


Dabei seit:
Beiträge: 5987
Herkunft: Leipzig

beantworten | zitieren | melden

Zitat von janDD
or leute ...daher würde ich gern irgendwie (und wenn es noch so absurd ist) meine daten in eine datatable haben ...
ist das sooooo unverständlich?
aber bitte nicht mehr eine datenbank vorschlagen ...

Du kannst mit den Füßen aufstampfen, oder sonstwas machen. Rumquengeln wird dir aber nicht viel Erfolg bringen. Wenn du die Ratschläge nicht annehmen willst, die dir gegeben werden, solltest du ein anderes Argument vorbringen als "or Leute".

Christian
Weeks of programming can save you hours of planning
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Angenommen Du schaffst es eine 2Gb Tabelle im Memory aufzubauen, dann wird es evtl. nur das Problem verlagern, so dass es dann in Deiner Berechnung knallt. X(
private Nachricht | Beiträge des Benutzers
weismat
myCSharp.de - Member



Dabei seit:
Beiträge: 878
Herkunft: Frankfurt am Main

beantworten | zitieren | melden

Da Du ADO.Net (System.Data) benutzt, sollte der Unterschied zwischen einer Datenbank, die einen ADO.NET Treiber hat und einer Csv-Datei nicht gross sein.
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10083

beantworten | zitieren | melden

@janDD:
Berechnungen?
Und warum speicherst du das dann als strings, wenn es doch zahlen sind?
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

Hallo,

ja, schon klar. Aber ich sagte doch mehrfach, dass ich die Datenbank-Alternative kenne, aber die Umsatz enormen Aufwand benötigt. Das hat nichts mit aufstampfen oder "die Vorschläge nicht umsetzen wollen" zu tun.

Ich habe eben in diversen Beiträgen im Netz gelesen, dass man mit Hilfe von memory mapped files solcher Speicherprobleme herr werden kann, aber kein Beispiel gefunden.


das wort "berechnungen" steht in diesem zusammenhang für die unterschiedlichsten analysen stichwort kontingenzanalyse, commonality analysen, kategoriale logistische korrelation ...

man kann nicht nur mit zahlen rechnen, wobei natürlich auch zahlen in der datatable stehen ...
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10083

beantworten | zitieren | melden

Nein Rechnen kann man nur mit Zahlen.
Man kann aber Daten Analysieren.

Dir bleibt nichts anderes Übrig als mal einen Schritt zurück zu machen und zu überlegen ob es wirklich sinnvoll ist weiterhin mit dem Kopf gegen die Wand zu rennen oder ab man nicht eher einen Weg um die Mauer findet.

1. Was sind das für Daten die mehrere Tausend Spalten benötigen?
2. Warum bestehst du auf DataTable? Die ist ein Speicherverschwender.
3. Wenn du dann einen wirklich geeigneten Datentyp gefunden hast, was lässt sich beim einlesen vereinfachen/zusammenfassen?
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Zitat
man kann nicht nur mit zahlen rechnen
Was meinst Du damit? Sind das Funktionen?
Gruß, CoLo
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7561
Herkunft: Waidring

beantworten | zitieren | melden

Hallo janDD,

ich schließe mich den anderen an, dass es günstigere Alternativen gibt, begonnen mit einem vernüftigen Design, das von sich auch schon Herr der Lage ist.
Dieser Punkt wurde aber wohl schon ausreichend diskutiert, so dass dies nicht erneut durchgekaut werden muss.
Zitat
memory mapped files ..., aber kein Beispiel gefunden.
Memory Mapped File Quirks
Ob und wie das bei einer CSV-Datei helfen soll weiß ich nicht. Ich denke Memory Mapped Files sind hier der falsche Weg.

mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
janDD
myCSharp.de - Member



Dabei seit:
Beiträge: 11

Themenstarter:

beantworten | zitieren | melden

man kann mit jedem mathematischen objekt rechnen, zum beispiel mit differentialformen, sogar mit objekten die man gar nicht wirklich hinschreiben kann (zum beispiel nicht riemann-integrierbare funktionen).

deine fragen sind natürlich alle richtig. würde ich das system von grund auf designen würde ich es nicht so machen. aber da ich schon sehr viel code (übernommen) habe, der eine datatable erfordert, würde ich ihn eben gern nachnutzen ...

aber danke für deine hilfe. ich würde den thread hiermit mal schließen...

@CoLo ... ein Beispiel habe ich ja schon des öfteren genannt: ANOVA
private Nachricht | Beiträge des Benutzers