SQLShack

Wir haben einige benutzerdefinierte Dateien, die wir von verschiedenen Anbietern erhalten, und für diese Situationen können wir keine Standard-ETL-Programme ohne jegliche Anpassung verwenden. Da wir unsere Möglichkeiten zum Lesen dieser benutzerdefinierten Dateien mit .NET erweitern, suchen wir nach effizienten Möglichkeiten zum Lesen von Dateien mit PowerShell, die wir in SQL Server-Job-Agenten, Windows-Task-Planern oder mit unserem benutzerdefinierten Programm, das PowerShell-Skripts ausführen kann, verwenden können. Wir haben viele Tools zum Parsen von Daten und wollten effiziente Möglichkeiten zum Lesen der Daten für das Parsen kennen, zusammen mit dem Abrufen bestimmter Datenzeilen aus Dateien nach Nummer oder nach der ersten oder letzten Zeile der Datei. Was sind einige Funktionen oder Bibliotheken, die wir zum effizienten Lesen von Dateien verwenden können?

Übersicht

Zum Lesen von Daten aus Dateien wollen wir uns im Allgemeinen auf drei Hauptfunktionen zur Erledigung dieser Aufgaben konzentrieren, zusammen mit einigen daneben aufgeführten Beispielen für diese in der Praxis:

  1. Wie man eine ganze Datei, einen Teil einer Datei oder einen Sprung in einer Datei liest. Es kann eine Situation auftreten, in der wir jede Zeile außer der ersten und letzten lesen wollen.
  2. Wie man eine Datei mit wenigen Systemressourcen liest. Wir haben vielleicht eine 100-GB-Datei, von der wir nur Daten im Wert von 108 KB lesen wollen.
  3. Wie man eine Datei so liest, dass wir die Daten, die wir brauchen, leicht parsen können oder dass wir Funktionen oder Tools verwenden können, die wir mit anderen Daten verwenden. Da viele Entwickler über String-Parsing-Tools verfügen, können wir durch das Verschieben von Daten in ein String-Format – wenn möglich – viele String-Parsing-Tools wiederverwenden.

Das oben Gesagte gilt für die meisten Situationen, die mit dem Parsen von Daten aus Dateien zu tun haben. Wir beginnen mit einer eingebauten PowerShell-Funktion zum Lesen von Daten und betrachten dann eine benutzerdefinierte Methode zum Lesen von Daten aus Dateien mithilfe von PowerShell.

PowerShell-Funktion Get-Content

Die neueste Version von PowerShell (Version 5) und viele frühere Versionen von PowerShell verfügen über die Funktion Get-Content, und mit dieser Funktion können wir die Daten einer Datei schnell lesen. Im folgenden Skript geben wir die Daten einer ganzen Datei auf dem Bildschirm der PowerShell ISE aus – ein Bildschirm, den wir im Laufe dieses Artikels zu Demonstrationszwecken verwenden werden:

1
Get-Content „C:\logging\logging.txt“

Get-Content gibt die gesamte Datei von logging.txt auf dem Bildschirm der PowerShell ISE aus (beachten Sie, dass das obige Bild nur einen Teil der vollständigen Datei darstellt).

Wir können diese gesamte Datenmenge in einer Zeichenkette speichern, die wir ourfilesdata nennen:

1
2

$ourfilesdata = Get-Content „C:\logging\logging.txt“
$ourfilesdata

Wir erhalten das gleiche Ergebnis wie oben, der einzige Unterschied ist, dass wir hier die gesamte Datei in eine Variable gespeichert haben. Es gibt allerdings einen Nachteil, wenn wir eine ganze Datei in eine Variable speichern oder eine ganze Datei ausgeben: wenn die Datei sehr groß ist, müssen wir die gesamte Datei in eine Variable einlesen oder die gesamte Datei auf dem Bildschirm ausgeben. Das fängt an, uns Performance zu kosten, wenn wir es mit größeren Dateien zu tun haben.

Wir können einen Teil der Datei auswählen, indem wir unsere Variable (Objekt ist ein anderer Name) wie eine SQL-Abfrage behandeln, bei der wir einige der Dateien statt der gesamten Datei auswählen. Im folgenden Code wählen wir die ersten fünf Zeilen der Datei aus, anstatt die gesamte Datei:

1
2

$ourfilesdata = Get-Content „C:\logging\logging.txt“
$ourfilesdata | Select-Object -Erste 5

Das Ausgabefenster von PowerShell ISE gibt nur die ersten fünf Zeilen der Datei zurück.

Wir können dieselbe Funktion auch verwenden, um die letzten fünf Zeilen der Datei abzurufen, wobei eine ähnliche Syntax verwendet wird:

1
2

$ourfilesdata = Get-Content „C:\logging\logging.txt“
$ourfilesdata | Select-Object -Letzte 5

Das Ausgabefenster von PowerShell ISE gibt nur die letzten fünf Zeilen der Datei zurück.

Die in PowerShell integrierte Funktion „Get-Content“ kann nützlich sein, aber wenn wir aus Gründen des Parsens nur sehr wenige Daten bei jedem Lesevorgang speichern möchten oder wenn wir zum Parsen einer Datei Zeile für Zeile lesen möchten, sollten wir die StreamReader-Klasse von .NET verwenden, mit der wir unsere Verwendung für eine höhere Effizienz anpassen können. Dies macht Get-Content zu einem großartigen Basisleser für Dateidaten.

Die StreamReader-Bibliothek

In einem neuen PowerShell-ISE-Fenster erstellen wir ein StreamReader-Objekt und entsorgen eben dieses Objekt, indem wir den folgenden PowerShell-Code ausführen:

1
2
3

$newstreamreader = New-Object System.IO.StreamReader(„C:\logging\logging.txt“)
### Hier wird die Datei gelesen
$newstreamreader.Dispose()

Im Allgemeinen ist es eine gute Praxis, jedes Mal, wenn wir ein neues Objekt erstellen, dieses Objekt zu entfernen, da dadurch Rechenressourcen für dieses Objekt freigegeben werden. Es stimmt zwar, dass .NET dies automatisch tut, aber ich empfehle trotzdem, dies manuell zu tun, da Sie vielleicht in Zukunft mit Sprachen arbeiten, die dies nicht automatisch tun, und es eine gute Praxis ist.

Es passiert nichts, wenn wir den obigen Code ausführen, weil wir keine Methode aufgerufen haben – wir haben nur ein Objekt erstellt und es entfernt. Die erste Methode, die wir uns ansehen werden, ist die Methode ReadToEnd():

1
2
3

$newstreamreader = New-Object System.IO.StreamReader(„C:\logging\logging.txt“)
$newstreamreader.ReadToEnd()
$newstreamreader.Dispose()

Die ReadToEnd()-Methode für StreamReader sieht identisch zu Get-Content im ISE-Fenster, indem sie alle Daten der Datei ausgibt.

Wie wir in der Ausgabe sehen, können wir wie bei Get-Content mit der ReadToEnd()-Methode alle Daten aus der Datei lesen; wie lesen wir die einzelnen Datenzeilen? In der Klasse StreamReader ist die Methode ReadLine() enthalten, und wenn wir diese anstelle von ReadToEnd() aufrufen, würden wir die erste Zeile der Daten unserer Datei erhalten:

1
2
3

$newstreamreader = New-Object System.IO.StreamReader(„C:\logging\logging.txt“)
$newstreamreader.ReadLine()
$newstreamreader.Dispose()

Die Methode ReadLine() liest die aktuelle Zeile der Datei, die in diesem Fall die erste Zeile ist.

Da wir dem StreamReader gesagt haben, dass er die Zeile lesen soll, hat er die erste Zeile der Datei gelesen und angehalten. Der Grund dafür ist, dass die Methode ReadLine() nur die aktuelle Zeile (in diesem Fall Zeile eins) liest. Wir müssen die Datei so lange lesen, bis wir das Ende der Datei erreicht haben. Woher wissen wir, wann eine Datei endet? Die Endzeile ist null. Mit anderen Worten, wir wollen, dass der StreamReader die Datei so lange liest (while-Schleife), bis jede neue Zeile nicht null ist (also Daten enthält). Um dies zu demonstrieren, fügen wir jeder Zeile, über die wir iterieren, einen Zeilenzähler hinzu, damit wir die Logik mit Zahlen und Text sehen können:

1
2
3
4
5
6
7
8

$newstreamreader = New-Object System.IO.StreamReader(„C:\logging\logging.txt“)
$eachlinenumber = 1
while (($readeachline =$newstreamreader.ReadLine()) -ne $null)
{
Write-Host „$eachlinenumber $readeachline“
$eachlinenumber++
}
$newstreamreader.Dispose()

Unser Zähler zeigt jetzt jede Zeile zusammen mit der Zeile des Textes der Datei an.

Während StreamReader jede Zeile liest, speichert er die Daten der Zeile in dem Objekt, das wir mit $readeachline erstellt haben. Auf diese Datenzeile können wir bei jedem Durchlauf String-Funktionen anwenden, z. B. die ersten zehn Zeichen der Datenzeile auslesen:

1
2
3
4
5
6
7

$newstreamreader = New-Object System.IO.StreamReader(„C:\logging\logging.txt“)
$eachlinenumber = 1
while (($readeachline = $newstreamreader.ReadLine()) -ne $null)
{
$readeachline.Substring(0,10)
}
$newstreamreader.Dispose()

StreamReader gibt die ersten zehn Zeichen jeder Datenzeile mit Hilfe der String-Methode Substring zurück.

Wir können dieses Beispiel erweitern und zwei weitere String-Methoden aufrufen – dieses Mal mit den String-Methoden IndexOf und Replace(). Wir rufen diese Methoden nur auf den ersten beiden Zeilen auf, indem wir nur die Zeilen kleiner als Zeile drei holen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

$newstreamreader = New-Object System.IO.StreamReader(„C:\logging\logging.txt“)
$eachlinenumber = 1
while (($readeachline = $newstreamreader.ReadLine()) -ne $null)
{
if ($eachlinenumber -lt 3)
{
Write-Host „$eachlinenumber“
$readeachline.Substring(0,12)
$readeachline.IndexOf(“ „)
$readeachline.Replace(“ „, „ersetzen“)
}
$eachlinenumber++
}
$newstreamreader.Dispose()

Die Substring(), IndexOf() und Replace() String-Methoden in den ersten beiden Zeilen der Datei.

Für das Parsen von Daten können wir unsere String-Methoden auf jede Zeile der Datei anwenden – oder auf eine bestimmte Zeile der Datei – bei jeder Iteration der Schleife.

Schließlich wollen wir in der Lage sein, eine bestimmte Zeile aus der Datei zu erhalten – wir können die erste und die letzte Zeile der Datei mit Get-Content erhalten. Wir verwenden StreamReader, um eine bestimmte Zeilennummer zu setzen, die wir an eine eigene Funktion übergeben, die wir erstellen. Wir werden eine wiederverwendbare Funktion erstellen, die eine bestimmte Zeilennummer zurückgibt. Als Nächstes wollen wir unsere Funktion zur Wiederverwendung einpacken, wobei wir zwei Eingaben benötigen: den Speicherort der Datei und die spezifische Zeilennummer, die wir zurückgeben wollen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Funktion Get-.FileData {
Param(
$ourfilelocation
, $specificline
)
Process
{
$newstreamreader = New-Object System.IO.StreamReader($ourfilelocation)
$eachlinenumber = 1
while (($readeachline = $newstreamreader.ReadLine()) -ne $null)
{
if ($eachlinenumber -eq $specificline)
{
$readeachline
break;
}
$eachlinenumber++
}
$newstreamreader.Dispose()
}
}
$savelinetovariable = Get-FileData -ourfilelocation „C:\logging\logging.txt“ -.specificline 17
$savelinetovariable

Wenn wir prüfen, gibt Zeile 17 „Die Pufferpoolerweiterung ist bereits deaktiviert. No action is necessary.“ korrekt zurück. Außerdem brechen wir die if-Anweisung ab – denn es besteht keine Notwendigkeit, die Datei weiter zu lesen, sobald wir die gewünschte Datenzeile erhalten haben. Außerdem beendet die dispose-Methode das Objekt, wie wir prüfen können, indem wir die Methode von der Befehlszeile in PowerShell ISE aufrufen, und sie gibt nichts zurück (wir könnten auch innerhalb der Funktion prüfen und würden das gleiche Ergebnis erhalten):

Abschließende Gedanken

Für die eigene Performance bietet StreamReader mehr Potenzial, da wir jede Zeile der Daten lesen und unsere zusätzlichen Funktionen nach Bedarf anwenden können. Aber wir brauchen nicht immer etwas zum Anpassen, und vielleicht wollen wir nur ein paar erste und letzte Datenzeilen lesen, in diesem Fall passt die Funktion Get-Content gut zu uns. Außerdem funktionieren kleinere Dateien gut mit Get-Content, da wir nie zu viele Daten auf einmal bekommen. Seien Sie nur vorsichtig, wenn die Dateien dazu neigen, mit der Zeit zu wachsen.

  • Autor
  • Recent Posts
Timothy Smith
Tim verwaltet hunderte von SQL Server und MongoDB Instanzen, und konzentriert sich in erster Linie darauf, die passende Architektur für das Geschäftsmodell zu entwerfen.
Er hat ein Jahrzehnt in der FinTech-Branche sowie einige Jahre in der BioTech- und Energietechnik gearbeitet. Er ist Gastgeber der West Texas SQL Server Users‘ Group, gibt Kurse und schreibt Artikel über SQL Server, ETL und PowerShell.
In seiner Freizeit engagiert er sich in der dezentralen Finanzbranche.
Alle Beiträge von Timothy Smith anzeigen

Timothy Smith
Neueste Beiträge von Timothy Smith (alle anzeigen)
  • Datenmaskierung oder Veränderung von Verhaltensinformationen – 26. Juni, 2020
  • Sicherheitstests mit extremen Datenvolumenbereichen – 19. Juni 2020
  • SQL Server-Leistungstuning – RESOURCE_SEMAPHORE-Wartezeiten – 16. Juni 2020

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.