|
» myCSharp.de Diskussionsforum |
|
|
|
Autor
 |
|
|
Hallo zusammen,
ich möchte Daten aus einer Datei einlesen. Diese möchte ich gerne in eine Liste<Person> eintragen lassen. Hinbekommen habe ich es, jedoch stehe ich noch am Anfang was meine Erfahrung in C# angeht und ich denke, dass man das folgende Beispiel effizienter lösen kann:
Die Datei hat folgenden Aufbau:
Code: |
1:
2:
3:
4:
|
Vorname; Nachname;
Daniel; Müller
...
Hans; Schneider |
|
C#-Code: |
class Person
{
public string Vorname{ get; set; }
public string Nachname{ get; set; }
}
|
C#-Code: |
string path = @"C:\Daten.csv";
var csvData = File.ReadAllLines(path)
.Skip(1)
.Select(x => x.Split(';'))
.Select(x => new Person()
{
Vorname = x[0],
Nachname = x[1]
});
var Person= new List<Person>();
foreach (var item in csvData)
{
Person.Add(new Person () {
Vorname = item.Vorname,
Nachname = item.Nachname
});
}
|
Ich hatte probiert an die LinQ Abfrage wie folgt zu erstellen:
C#-Code: |
List<Person> csvData = File.ReadAllLines(path)
.Skip(1)
.Select(x => x.Split(';'))
.Select(x => new Person()
{
Vorname = x[0],
Nachname = x[1]
}).ToList();
|
Was leider nicht erfolgreich war (auch andere diverse Formen haben nicht funktioniert).
Was mache ich falsch? Wie bekomme ich die Daten sofort in die List vom Typ Person?
Noch eine andere Fragen:
- Mir ist beim Ausführen aufgefallen, dass ich mal eine Fehlermeldung bekommen habe, dass auf die Datei nicht zugegriffen werden kann da diese von einem anderen Prozess verwendet wird (ich hatte die Datei geöffnet). Daher würde es vermutlich Sinn machen zu prüfen ob die Datei "zur Verfügung" steht. Was würde man noch prüfen, bevor man mit dem File.ReadAllLines auf die Datei zugreift?
Danke für eure Hilfe!
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von mchrd am 08.11.2018 08:29.
|
|
08.11.2018 08:27
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Spook
myCSharp.de-Mitglied
Dabei seit: 28.10.2008
Beiträge: 221
Entwicklungsumgebung: VS2019 Herkunft: Esslingen a.N.
|
|
Zitat von mchrd: |
Was würde man noch prüfen, bevor man mit dem File.ReadAllLines auf die Datei zugreift? |
Hallo mchrd,
eine vorangestellt Prüfung macht keinen Sinn, da selbst wenn deine Prüfung positiv ist, kann die Datei zwischen deiner Prüfung und deinem Zugriff gelöscht oder gelockt werden. In diesem Fall bekommst du wieder eine Exception ... bist also genau so weit wie vorher. Daher einfach versuchen die Datei zu öffnen, so wie du es bereits jetzt machst.
Kleiner Verbesserungsvorschlag:
Anstatt File.ReadAllLines könntest du File.ReadLines verwenden, so dass nicht erst die komplette Datei geladen werden muss.
Grüße
spooky
|
|
08.11.2018 10:14
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.464
Herkunft: BW
|
|
Was ist effizienter für Dich? ;-)
Linq ist nicht immer hübscher - und auch nicht immer performanter.
Zitat: |
Daher würde es vermutlich Sinn machen zu prüfen ob die Datei "zur Verfügung |
Das Prüfen ist nur möglich, indem Du die Exception abfängst. Davon abgesehen macht bei solchen Zugriffen ein vorheriges Prüfen kein Sinn, weil trotzdem die Situation auftreten könnte, dass zwischen Prüfung und Zugriff jemand anderes die Datei lockt - wenig wahrscheinlich aber theoretisch möglich.
Mein Rat: lass Linq erst mal weg und bau lieber erst mal stabilen, gut strukturierten Code.
|
|
08.11.2018 10:15
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Hallo,
ich danke euch herzlich für eure Antworten! Diese haben mir wirklich geholfen.
@p!lle
Danke für den Link zu dem guten Artikel. Dass der Typ csvData Person ist wusste ich, da ich aus dem anonymen Typ new ein new Person
C#-Code: |
.Select(x => new Person()
|
gemacht hatte. Nur wollte ich es gerne in einer generischen Liste haben (List<Person>) haben. Der Artikel hat mir aber dennoch geholfen, da ich jetzt einige Dinge dazu gelernt habe.
Ich habe das mit dem zweiten Code durchgespielt und es hat wirklich funktioniert. Ich hatte einen anderen Fehler (dort hatte ich eine Datei verwendet, welche eine Spalte weniger hatte. Danke für den Hinweis!
@Spook
Stimmt das ist ein wirklich guter Einwand (wenn zwischen der positiven Prüfung und dem Aufruf die Datei doch noch von einem anderen Prozess blockiert wird).
Das File.ReadLines zu verwenden ist ein super Hinweis! Danke! Ich habe noch ein wenig nachgelesen und es macht absolut Sinn. Danke!
@Abt
Nein das stimmt Linq ist nicht unbedingt immer performant. Bei SQL Abfragen habe ich das bereits mehrfach gelesen, dass komplexe Abfragen nicht unbedingt derart optimiert sind, dass sie mit einer guten SQL Abfrage mithalten können.
Kannst du mir noch einen Tipp geben, mit welcher Klasse ich die CSV Datei sonst einlesen kann? Da ich beim googlen i.d.R. immer nur Linq-Abfragen finde.
Danke auch für deine Hilfe!
Ich wünsche euch allen einen schönen Abend
|
|
09.11.2018 17:00
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.464
Herkunft: BW
|
|
LINQ ist nicht SQL.
Zitat von mchrd: |
Bei SQL Abfragen habe ich das bereits mehrfach gelesen, dass komplexe Abfragen nicht unbedingt derart optimiert sind, dass sie mit einer guten SQL Abfrage mithalten können. |
Das sind zwei paar Stiefel, die erst mal nichts miteinander zutun haben.
Da solltest Du evtl. nochmal genauer lesen ;-)
Zitat von mchrd: |
Kannst du mir noch einen Tipp geben, mit welcher Klasse ich die CSV Datei sonst einlesen kann? Da ich beim googlen i.d.R. immer nur Linq-Abfragen finde. |
Linq liest Dir nicht Dein CSV ein. Auch bei Deinem Code nicht.
Du kannst problemlos auf Linq in Deinem Code verzichten und trotzdem identisch Dein CSV einlesen.
Und dass Du "nur Linq" in Zusammenhang mit CSV findest - bezweifle ich sehr stark.
Vielleicht solltest Du auch noch mal lesen, was Linq ist ;-) Gerade Einsteiger tun sich oft schwer mit Linq; weil das Wissen fehlt, was Linq ist - und was nicht.
-> Linq ist nichts anderes als ein funktionaler Ansatz in .NET, um gewisse Schreibweisen zu vereinfachen.
Alles in Linq besteht aber im Endeffekt aus Schleifen und Bedingungen.
Ein Einstieg dazu: Besserer C#-Code durch funktionale Programmierung
Aber wie gesagt; übersichtlicher oder schneller ist Linq nicht unbedingt; letzteres aber auch in dem Delta nicht relevant.
|
|
09.11.2018 21:57
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Guten Morgen Abt,
danke für deine Antwort.
Zitat von Abt: |
LINQ ist nicht SQL.
Zitat von mchrd: |
Bei SQL Abfragen .... |
Das sind zwei paar Stiefel, die erst mal nichts miteinander zutun haben.
Da solltest Du evtl. nochmal genauer lesen ;-) |
Ok, dann werde ich das tun. Wird aber sicher noch einige Zeit in Anspruch nehmen bis ich den Stand habe.
Zitat von Abt: |
Zitat von mchrd: |
Kannst du mir noch einen Tipp geben, mit welcher Klasse ich die CSV Datei sonst einlesen kann? Da ich beim googlen i.d.R. immer nur Linq-Abfragen finde. |
Linq liest Dir nicht Dein CSV ein. Auch bei Deinem Code nicht. Du kannst problemlos auf Linq in Deinem Code verzichten und trotzdem identisch Dein CSV einlesen. Und dass Du "nur Linq" in Zusammenhang mit CSV findest - bezweifle ich sehr stark. |
Wenn ich es richtig verstanden habe, gehören die Funktionen Split(), Select(), Skip() zu LINQ. Dann ist es wirklich so, dass ich auf StackOverflow überall mindestes eine der Funktionen gefunden habe. Selbst wenn ich nach "C# read csv file without linq" eingebe, komme ich auf derarige Seiten mit linq Lösungen. Ich finde zwar auch so Ausdrücke wie:
C#-Code: |
var query = from line in File.ReadLines(filename)
let csvLines = line.Split(';')
from csvLine in csvLines
where !String.IsNullOrWhiteSpace(csvLine)
let data = csvLine.Split(',')
select new
{
ID = data[0],
FirstNumber = data[1],
SecondNumber = data[2],
ThirdNumber = data[3]
};
|
Aber selbst hier ist ja das "Split" von LINQ oder verstehe ich das völlig falsch?
Zitat von Abt: |
Vielleicht solltest Du auch noch mal lesen, was Linq ist ;-) Gerade Einsteiger tun sich oft schwer mit Linq; weil das Wissen fehlt, was Linq ist - und was nicht.
-> Linq ist nichts anderes als ein funktionaler Ansatz in .NET, um gewisse Schreibweisen zu vereinfachen.
Alles in Linq besteht aber im Endeffekt aus Schleifen und Bedingungen. |
Ja wahrscheinlich habe ich da noch viel bedarf und muss noch einiges lernen um es wirklich zu verstehen.
[quote=Abt]Ein Einstieg dazu: Besserer C#-Code durch funktionale Programmierung
Aber wie gesagt; übersichtlicher oder schneller ist Linq nicht unbedingt; letzteres aber auch in dem Delta nicht relevant.[/CSHARP]
Danke für den super Link. Von der Basta Konferenz gibt es ja einige interessante Videos.
Danke für deine/eure Hilfe!
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von mchrd am 11.11.2018 08:08.
|
|
11.11.2018 08:08
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
T-Virus
myCSharp.de-Mitglied
Dabei seit: 17.04.2008
Beiträge: 1.691
Entwicklungsumgebung: Visual Studio, Codeblocks, Edi Herkunft: Nordhausen, Nörten-Hardenberg
|
|
Split ist eine Methode der String Klasse und gehört nicht zu Linq.
Aber den Code kannst du auch komplett ohne Linq mit einer einfachen Schleife bauen.
C#-Code: |
string[] lines = File.ReadLines(filename);
foreach(string line in lines)
{
if(String.IsNullOrEmpty(line))
continue;
string[] data = line.Split(';');
Person person = new Person
{
ID = data[0],
FirstNumber = data[1],
SecondNumber = data[2],
ThirdNumber = data[3]
};
}
|
An einigen Stellen ist Linq wirklich sehr hilfreich, aber sobald man mit großen Listen/Arrays o.ä. arbeitet und viele Operationen darüber laufen lassen muss, sollte man sich überlegen ob Linq sinnvoll ist.
Das ganze Konzept von Linq basiert, wie von Abt schon angesprochen hat, auf Schleifen mit Iteratoren.
Es gibt Fälle wo man mit einer einfachen Schleife und somit nur einem einmaligem Durchlauf über die Liste eine bessere Lösung bekommt als mit n-Linq Operationen, da hier durch die einzelnen Zwischenergebnisse durchlaufen werden muss, was dann eben deutlich länger dauert.
Aber bei kleineren/mittleren Listen kann man auch mit Linq gut arbeiten.
Die Einsparungen ohne Linq wären dann für viele Fälle zu klein.
T-Virus
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von T-Virus am 11.11.2018 15:09.
|
|
11.11.2018 10:32
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.464
Herkunft: BW
|
|
Zitat von mchrd: |
Aber selbst hier ist ja das "Split" von LINQ oder verstehe ich das völlig falsch? |
Wenn sich Dir diese Frage stellt und Dir damit unsicher bist, dann schau in die Doku: String.Split Method
Da siehst Du schon an Namespace, dass diese eben NICHT zu Linq gehört, wie auch T-Virus schon sagte.
|
|
11.11.2018 14:37
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Zitat von T-Virus: |
Es gibt Fälle wo man mit einer einfachen Schleife und somit nur einem einmaligem Durchlauf über die Liste eine bessere Lösung bekommt als mit n-Linq Operationen, da hier durch die einzelnen Zwischenergebnisse durchlaufen werden muss, was dann eben deutlich länger dauert. |
Es ist mitnichten so, dass pro Linq-"Operation" einmal die Liste und alle Zwischenergebnisse durchlaufen wird, wo hast du denn den Unsinn her?
LaTino
|
|
12.11.2018 08:11
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.464
Herkunft: BW
|
|
LaTino, der einzige Fall, der mir dazu aktuell einfällt den er meinen könnte wäre, wenn jedes Linq-Segment durch ein ToList aktiv materialisiert wird.
|
|
12.11.2018 10:21
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.464
Herkunft: BW
|
|
Zitat von T-Virus: |
Wenn man diese bei großen Listen, Arrays o.ä. zusammen hängt, muss auch zusätzlich iterriert werden. |
Nein.
|
|
12.11.2018 11:52
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
T-Virus
myCSharp.de-Mitglied
Dabei seit: 17.04.2008
Beiträge: 1.691
Entwicklungsumgebung: Visual Studio, Codeblocks, Edi Herkunft: Nordhausen, Nörten-Hardenberg
|
|
Wenn ich mir den Referenz Code für die Erweiterungsmethoden anschaue, dann sehe ich dort die interne Verwendung der entsprechenden Iteratoren.
Damit also die Daten entsprechend bei Select, Where und GroupBy bereitstehen, muss immer über die jeweilige Untermenge gelaufen werden.
Das ist aber eben nur bei zwei Fällen ein Problem.
1.Großen Datenmengen, ab einigen Mio. Datensätzen
2.Beim durchlaufen von Schleifen mit entsprechenden Listen für Filterungen von Daten
In beiden Fällen hat man ab einem bestimmten Punkt einfach einen Overhead durch die ganzen Iteratoren.
Das ist aber eben nur dann der Fall, wenn es um Performance geht.
Dies sind dann aber eben Sonderfälle wo es wirklich um jede Sekunde geht.
In 99% der Fälle kann Linq ohne bedenken nutzen kann.
Link:
https://referencesource.microsoft.com/#S...e5ab1a57b7e1438
T-Virus
|
|
12.11.2018 12:51
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Hallo T-Virus,
ich denke du hast dich da in etwas verrannt bzw. falsch aufgefasst.
LinQ arbeitet wie ein Fließband und nach dem Pull-Prinzip, d.h. der jeweilige Verbraucher holt sich die nächsten Daten und dabei wird nur soweit iteriert wie nötig. LinQ arbeitet daher grundsäztlich nach O(n), wobei auch nur einmal über die gesamte Quelle iteriert wird -- wie schon angemerkt worden ist.
Grob schaut es dabei immer so aus:
C#-Code: |
public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> filter)
{
foreach (T item in source)
if (filter(item))
yield return item;
}
|
Im Quellcode (entweder der Reference-Source od. jener in corefx auf GitHub) schaut es ein wenig anders aus, da sämtliche Optimierungen und Spezialisierungen (wie wenn IEnumerable<T> ein Array ist, etc.) angewandt worden sind. Genauso wird im Quellcode oft auf yield return verzichtet und stattdessen der Iterator selbst programmiert ( yield return wird vom C#-Compiler in den entsprechenden Iterator umgeschrieben).
mfG Gü
|
|
12.11.2018 16:27
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Hallo zusammen,
zunächst einmal vielen Dank T-Virus für das Beispiel!
Allerdings musste ich aus "string[] lines"
C#-Code: |
string[] lines = File.ReadLines(filename);
foreach(string line in lines)
{
if(String.IsNullOrEmpty(line))
continue;
string[] data = line.Split(';');
|
ein "var lines" machen, dass es compiliert wurde. Also implizit die Variable lines deklarieren und den Compiler entscheiden lassen, welchen Typ die Variable hatte.
Anhand eurer Diskussionen merke ich, wie wenig Ahnung ich von C# habe und wie viel ihr könnt!
Danke nochmal an alle die hier so ausführlich erklären!
|
|
18.11.2018 17:34
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
|