Batch Datei in mehrere kleine Dateien aufteilen

aggitron

Commander
Registriert
Jan. 2006
Beiträge
2.075
Hallo,

ich stehe vor einem Problem welches mit einem Batch Skript gelöst werden muss.

Ich habe eine Datei mit folgendem beispielhaftem Aufbau:

Code:
quelle.txt

01viele Zahlen
02viele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlenviele Zahlen
01viele Zahlenviele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlen
01viele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlen

Wie man sieht besteht die Datei aus einer beliebig großen Anzahl von aneinander gereihten Blöcken. Ein Block besteht aus genau einer Kopfzeile beginnend mit 01 und beliebig vielen Informationszeilen beginnend mit 02. Ich möchte nun für jeden Block eine einzelne Datei schreiben.
Was wäre hier der Ansatz?

Ausgabe:

Code:
datei1.txt

01viele Zahlen
02viele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlenviele Zahlen

datei2.txt

01viele Zahlenviele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlen

datei3.txt

01viele Zahlenviele Zahlenviele Zahlen
02viele Zahlenviele Zahlenviele Zahlen
 
Angenommen die Datensätze würden folgende Konstellation aufweisen, dann ist die Stapelverarbeitung sinnlos, da dann an folgenden Stellen ebenfalls aufgeteilt werden würde:

01viele Zahlen
02viele Zahlenviele Zahlenviele 01Zahlen
02viele Zahlenviele Zahlen01viele Zahlenviele Zahlen
02viele Zahlenviele Zahlen
01viele Zahlenviele Zahlenviele Zahlenviele Zahlen
 
Du hast recht. Ich habe aber festgestellt das in der Kopfzeile die ersten 10 Zahlen immer gleich sind. Das sollte doch das Problem auflösen, oder?
 
Mit Hilfe von RegEx kann man nur den Zeilenanfang nach "01" und "02" durchsuchen. Am beste nutzt man dazu perl:

Code:
#!/usr/bin/perl

# RegEx in Perl:
# https://wiki.selfhtml.org/wiki/Perl/Regul%C3%A4re_Ausdr%C3%BCcke

# Datein lesen und schreiben:
# https://de.wikibooks.org/wiki/Perl-Programmierung:_Dateien

@zeilen = ("");
$index = 0;
open(infile, "<quelle.txt");

while (<infile>) {
	push(@zeilen, $_);
}
close(infile);
for (@zeilen) {
	if (/^01/) {
		if ($index > 0) {
			close($outfile);
		}
		$index++;
		$datei = "datei${index}.txt"; # http://alvinalexander.com/perl/edu/articles/pl010003.shtml
		open($outfile, ">>", $datei) or die "Kann Datei $datei nicht zum Schreiben öffnen: $!";
		print $outfile $_;
	}
	if (/^02/) {
		print $outfile $_;
	}
}
close($outfile);

Den Code speichert man in einer Datei (z.B. convert.pl) und führt ihn mit 'perl convert.pl' aus. Optional kann man den Code noch anpassen, dass man beliebige Dateien mit 'perl convert.pl quelle.txt' umwandeln kann.

Die Dateien werden nicht überschrieben, sondern nur um zusätzliche Zeilen ergänzt. Ersetzt man Zeilen 24 & 25 durch den folgenden Code, dann werden die Dateien überschrieben:

Code:
open($outfile, ">", $datei) or die "Kann Datei $datei nicht zum Schreiben öffnen: $!";
print $outfile $_;
close $outfile;
open($outfile, ">>", $datei) or die "Kann Datei $datei nicht zum Schreiben öffnen: $!";

Wenn ich später etwas Zeit habe, dann füge ich noch ein paar Kommentare hinzu.
 
Zuletzt bearbeitet:
Perl steht mir nicht zur Verfügung. Es muss per Batch Skript gelöst sein.

Oder verstehe ich dich falsch?
 
Teste mal folgendes:
Code:
@echo off
REM verzögerte Befehlerweiterung aktivieren, damit die Variablen temp und lfdnr in der Schleife auch veränderlich sind
setlocal enabledelayedexpansion

REM Setze lfdnr=0
set /A "lfdnr=0"

REM in der For-Schleife jede Zeile einzeln durchgehen, "delims=", damit nicht bei Leerzeichen getrennt wird (Standardeinstellung)
for /F "delims=" %%a in (quelle.txt) do (
REM da eine Konstruktion à la "%%a:~0,2" für die ersten zwei Zeichen der Variable nicht funktioniert, der Umweg über temp
set "temp=%%a"
REM Wenn die ersten zwei Zeichen der neuen Zeile "01" sind, dann erhöhe lfdnr um eins
if "!temp:~0,2!"=="01" set /A lfdnr=lfdnr+1
REM gebe die aktuelle Zeile in die Datei "datei#.txt" aus, wobei # die lfdnr ist; >> bedeutet, dass an die Datei angehängt wird und nicht überschrieben.
echo %%a>>datei!lfdnr!.txt
)
Es dürfen halt vorher keine Dateien mit Namen datei#.txt vorhanden sein, sonst wird an die einfach angefügt.
Ansonsten könntest du es statt Batch evtl. auch mit Powershell versuchen, ist ja auch Windows-Bordmittel.
 
Zuletzt bearbeitet: (Kommentare eingefügt.)
Das scheint super zu funktionieren. Kannst du es mal kommentieren? Man will ja dabei auch was lernen. ;)
 
Freut mich, dass es läuft. Ich hab oben mal Kommentare eingefügt. Leider sind die Codetags im Forum nicht auf Batch ausgelegt, deshalb sieht das oben etwas unübersichtlich aus.
 
Stand da nicht vorher [Bash] im Titel, oder habe ich mich nur verlesen? Bin halt von einem Unix-Rechner ausgegangen.

Perl kann man aber auch ganz einfach unter Windows nutzen. Strawberry Perl gibt es als portable Version. Dieses enthält eine bat-Datei, die man startet und schon kann man Perl-Scripte ausführen.

Hier ist noch eine etwas bessere Version von meinem Script:

Code:
#!/usr/bin/perl

use strict;
use warnings;
 
# RegEx in Perl:
# https://wiki.selfhtml.org/wiki/Perl/Regul%C3%A4re_Ausdr%C3%BCcke
 
# Datein lesen und schreiben:
# https://de.wikibooks.org/wiki/Perl-Programmierung:_Dateien

# Argumente des Script-Aufruf auslesen 'perl convert.pl quelle.txt'
# shift ist notwendig, da convert.pl das erste Argument ist
my $in = shift @ARGV; # $ zeigt an, dass es sich um ein Skalar handelt
if ( ! defined $in ) {
    die "Es wurde kein Argument übergeben.";
}
my $prefix = shift @ARGV;
if ( ! defined $prefix ) {
    die "Es wurde nur ein Argument übergeben.";
}

my @zeilen = (""); # Arrays beginnen mit einem @
my $index = 0;
# der Teil rechts von or wird ausgeführt, wenn open einen Fehler meldet
# die gibt die Fehlermeldung aus und beendet das Programm
open(my $infile, "<", $in) or die "Kann Datei ${in} nicht zum Lesen öffnen: $!";

#  Die Datei wird Zeilenweise eingelesen und in einem Array gespeichert;
# man kann auch direkt das tun, was ich in eine weitere for-Schleife ausgelagert habe
while (<$infile>) { 
	push(@zeilen, $_);
}
close($infile);

my $outfile;
for (@zeilen) {
	if (/^01/) { # reguläre Ausdrücke sind von / umschlossen; ^ steht für Zeilenanfang
		if ($index > 0) {
			close($outfile);
		}
		$index++;
		# wie bei Bash-Scripten kann man Variablen einfach in Strings reinschreiben;
		# das gilt nur bei " und nicht bei '
		my $datei = "${prefix}${index}.txt";
		open($outfile, ">", $datei) or die "Kann Datei ${datei} nicht zum Schreiben öffnen: $!";
		print $outfile $_;
		close $outfile;
		open($outfile, ">>", $datei) or die "Kann Datei ${datei} nicht zum Schreiben öffnen: $!";
	}
	if (/^02/) {
		print $outfile $_;
	}
}
close($outfile);

Damit würde der Aufruf so aussehen:

Code:
perl convert.pl quelle.txt datei

Es werden dann die neuen Dateien datei1.txt, datei2.txt und datei3.txt erstellt.
 
Zuletzt bearbeitet:
Ne, da stand schon immer Batch. ;)

Trotzdem danke für deine Mühen!
 
Ich habe Beitrag #10 noch etwas überarbeitet.

@aggitron
Ich lerne im Moment Perl, deshalb sehe ich das gegebene Problem als passende Übungsaufgabe an. Es ist somit nicht schlimm, wenn du es nicht ausprobierst.

Das Wikibook zu Perl ist ein ganz guter Einstieg. Ab dem Abschnitt Kontrollstrukturen wird leider nicht mehr so ausführlich erläutert. Kennt man schon eine andere Programmiersprache, kommt man mit den kurzen Erläuterungen aus.
 
Zurück
Oben