Datei in mehrere kleine Dateien umwandeln

Bitte aktiviere JavaScript!
Hallo zusammen,
ich versuche eine Aufgabe zu lösen die Aufgabe ist folgende:

Filesplit
Möchte man eine große Datei auf Diskette kopieren, dann besteht bei Dateien über 1,44 MB ein Problem. Man könnte dann aus einer großen Datei mehrere kleine "diskettenkompatible" machen. Schreibe für das Problem ein Programm, welches sich etwa so auf der Kommandozeile aufrufen lässt:
$ java Filesplit Datei.exe
Ist zum Beispiel die Datei 2,44 MiB groß, dann wird das Programm daraus die Dateien Datei.exe.1 und Datei.exe.2 mit den Größen 1,44 MiB und 1 MiB machen.
Derzeit zerbreche ich mir leider noch den Kopf wie ich es hin bekomme!
Bitte kommt jetzt nicht mit Kommentaren wie denk weiter drüber nach :p <- destruktive Kommentare bekomme ich schon genug von anderen :(

Es ist noch beim Konzept. Meine Frage in erster Linie, ist wie bekomme ich die Größe hin also wie hier z. B. auf 1,44 Mib?
Wollte da erst eine Methode schreiben die mir eine Anzahl an Schleifen Durchläufe zurück gibt. Sprich ich Buffere den InputStream mit 4096 Bytes = 4 Kbyte dann rechne ich die 1,44 MByte/4 Kbyte und sollte sofern das Korrekt gerechnet ist auf 170,24 = 171 Durchläufe kommen.
Jetzt wollte ich das Programm aber nicht nur mit 1,44 MByte laufen lassen sondern die größe soll mit angegeben werden können :)
Wie bekomme ich das nun hin. Habe mir die File API auch angeschaut um die Metadatei auszulesen doch dort habe ich leider keine passende Methode gefunden oder ich bekomme es nicht zusammen. Denn was ich bräuchte wäre eine Methode die mir die Größe der Datei zurück geben würde dann könnte man das ganze vereinfachen :) man fragt bei jedem Durchlauf ist die Datei derzeit 1,44 Mib groß.

Wenn die Frage irgendwie zu doof ist dann "einfach" ignorieren Okay :)
Dann schau ich weiter. Doch ich sitze (schande über mich :D) schon seit 11:00 Uhr davor und bekomme es nicht zusammen.

Ich hoffe Ihr könnt mir Helfen :)

LG
 
A

Anzeige


Vielleicht hilft dir dieser Kurs hier weiter: (hier klicken)
Also Du hast doch schon einen guten Ansatz. Genau so solltest Du heran gehen.

Aber wichtig: Nicht aufrunden sondern abrunden. Wenn du genau 1,44 Mib Platz hast und du 171 Durchläufe zu 4Kbyte machst, dann bist Du ja drüber. Also da wirklich gut aufpassen!

Und du brauchst auch nicht wirklich die Größe der original Datei, denn Du kannst die Datei öffnen und immer wieder versuchen, deinen Buffer zu füllen. Du bekommst da in der Regel bei Binary Read Operationen zurück, wieviele Bytes gelesen wurden. Du führst die Schleife so lange durch, bis Du weniger als Deinen Block an Daten bekommst.

Und in dieser Schleife prüfst Du, der wievielte Durchgang es ist. Ist die Anzahl der gewünschten Blöcke erreicht, dann schließt Du die Ziel-Datei und öffnest die nächste Zieldatei. (Also z.B. ein <Original-Filename>.<Zähler>)

Also eigentlich relativ einfach. Ginge vom Code her noch einfacher, indem Du einen Block reservierst, der genau der Zielgröße entspricht. Dann hast Du weniger Abfragen und mit kleinen Zieldateien funktioniert es gut. Nur wenn Du dann eine Datei von 20TB in 4TB große Blöcke zerteilen willst, um diese Datei auf mehreren 4TB großen Platten zu transportieren, dann hast Du ein Problem :)
 
Aber wichtig: Nicht aufrunden sondern abrunden. Wenn du genau 1,44 Mib Platz hast und du 171 Durchläufe zu 4Kbyte machst, dann bist Du ja drüber. Also da wirklich gut aufpassen!
Das wäre das erste was ich schon verhauen hätte :D nicht nachgedacht.
Doch das nächste ist auch das umrechnen bzw. das rechnen wie oft die Schleifen laufen darf denn das brauch ich ja weiterhin.
Ich fülle den Buffer mit 4096 Bytes so und die Datei ist jetzt 20 MByte groß. Wie rechne ich das jetzt um.
Auf dem Papier geht das ja ganz Leicht mit 2er Potenzen :)
20 x M x Byte = 20 * 2(hoch 20) * 2(hoch 3)
4 x K x Byte = 2(hoch 2) * 2(hoch 10) * 2(hoch3)
dann kürzt man wild und erhält seinen Count :)
Wenn ich das jetzt aber Programmieren müsste wüsste ich ehrlich gesagt gerade überhaupt nicht wie :(

LG

moment geht das mit dem Objekt File dann die größe der Datei und das Teile ich einfach durch die größer es Buffer :) mal gleich nach schauen :p
 
Zuletzt bearbeitet:
Du musst dir vorher nicht ausrechnen, wie oft deine Schleife laufen muss.
Nimm eine while-Schleife, die so lange 4kB-Blöcke aus der Eingabedatei ausliest, bis das Ende der Datei erreicht wurde.

Java:
        byte[] buffer;
        while ((buffer = deinFileInputStream.readNBytes(blockSize)).length != 0) {
            // schreibe Buffer in Teildatei
        }
 
Okay dann läuft die Schleife bis die Datei am ende ist :) das ist schon mal sehr schön.
Doch dann habe ich ja immer noch das Problem WANN FÄNGT DIE NEUE DATEI AN.
Ich müsste ja eine gewisse Dateigröße haben, also hier die 1,44 Mbyte und das durch die 4kByte teilen oder geht das auch anders?
Denn so zu rechnen ist irgenwie auch etwas umständlich finde ich das sind ja alles super große Zahlen geht das nicht leichter?
lg
 
Wieso setzt du deine Blocksize nicht einfach auf 1,44 MB? Dann erzeugst du mit jedem Schreibvorgang eine neue Datei.
 
Ich soll also ein Array anlegen das 1,44 MB speichert diese dann lesen und im Hauptspeicher Buffern.
Dann werden die Bytes über die Schleife in eine neue Datei geschrieben habe ich das jetzt richtig verstanden.
Die Methode finde ich nur nicht readNBytes(...).
Desweiteren ist es ja okay für 1,44 MB aber was ist wenn es 2 GB wären? soll ich die in den Hauptspeicher auf einmal laden?
LG
 
Die Methode gibts erst seit Java 9. Vermutlich hast du noch ein älteres JDK, kannst du aber ruhig mal updaten, wir sind schon bei Java 11 ;)

Bei der Aufgabenstellung ist es ja eher unwarscheinlich, dass du auf einmal eine Datei in mehrere 2GB-Dateien teilst, aber gut.
Wenn du deinen Buffer kleiner wählst, als die Zielgröße der Datei, dann musst du eben mitzählen wie viele Bytes du schon geschrieben hast.

Mit der Methode sollte das kein Problem sein:
https://docs.oracle.com/javase/8/docs/api/java/io/FileOutputStream.html#write-byte:A-int-int-

Wenn du mit dem Inhalt deines aktuellen Buffers beim Schreiben die Zielgröße erreichen würdest, schreibst du nicht den kompletten Buffer, sondern nur bis zu einer bestimmten Länge (dafür der len-Parameter). Den Rest des Buffers schreibst du dann in die neue Datei (offset-Parameter).

Musst halt nur immer aufsummieren, wie viele Bytes du schon in die aktuelle Datei geschrieben hast, dann weiß du auch, wie viele noch fehlen.
 
Das mit dem mit zählen der bytes hört sich glaube gut an :)
2GByte sind dann 2*2(hoch 30) Bytes. Soll ich das dann vorher berechnen? Mit der pow Funktion.
Ich tu mir bei der Aufgabe echt schwer wenn nicht schau ich morgen nochmal nach mit einem klaren kopf!
LG
 
Also wieso machst du es dir so kompliziert? Du kannst doch erst einmal als Parameter angeben lassen, wieviel KB jede einzelne Datei groß sein soll. Dann kann der Benutzer rechnen.

Also x KB bedeutet, dass du x mal 1024 Byte Blöcke in eine Datei schreiben willst.

Aber selbst wenn es andere Größen sein sollen, ist es doch einfach:
1MB = 1024 KB
1 GB = 1024 MB
Also einfach immer mal 1024 nehmen...
 
Du möchtest g Bytes lesen. Ein Puffer der Größe g ist im Allgemeinen zu groß, daher verwendest Du einen Puffer mit einer Größe p < g.

Bei jedem Lesevorgang
1. kannst Du angeben, wie viele Bytes höchstens gelesen werden sollen
2. wird zurückgegeben, wie viele Bytes tatsächlich gelesen wurden.

Du kannst also zu jeder Zeit angeben, wie viele Bytes noch zu lesen sind. Benennen wir diese Angabe mal mit r. Zu Beginn gilt natürlich r = g und in jeder Iteration verringert sich r um die Zahl der gelesenen Bytes. Du willst in jeder Iteration also so viel Bytes wie möglich lesen. Möglich ist in der Regel p, außer falls r < p gilt.

Wäre mal ein Ansatz.
 
Leider komme ich mit euren Vorschlägen überhaupt nicht wirklich weiter deswegen habe ich das versucht mit dem Rechnen doch da hänge ich jetzt auch :(

Hier mal mein bisheriger versuch:
Java:
package Insel;
import java.io.*;

public class Filesplit {
    public static long loopRuns(double splitLength, int bufferLength) {
        return (long) (splitLength/bufferLength);
    }
   
    public static void main(String...strings ) throws IOException {
        InputStream is = null;
        OutputStream os = null;
        try {
            File f = new File("/home/test");
            is = new FileInputStream("/home/test");
            byte[] buffer = new byte[4096];
            int countName = 0;
            int bufferLength = buffer.length;
            double splitLength = 1.33*1024*1024;        //1.33 M
            long runs = loopRuns(splitLength,bufferLength);
            String outputPath = "/home/a";
           
            System.out.println(f.length()+" "+buffer.length+" "+runs);
                       
            if(f.isFile()) {
                if(f.length() < buffer.length) {
                    os = new FileOutputStream(outputPath);
                    int b = is.read(buffer);
                    while (b != -1) {
                        os.write(buffer, 0, b);
                        b = is.read(buffer);
                    }
                }
                else {
                    countName++;
                    outputPath+=""+countName;
                    os = new FileOutputStream(outputPath);
                    int b = is.read(buffer);
                   
                }
            }
            else if(f.isDirectory()) {
                System.out.println("");
            }
                else throw new Exception("unknow error");
           
        }
        catch(IOException ex) {
            ex.getMessage();
        }
        catch(Exception ex) {
            ex.getMessage();
        }
        finally {
            if(is!=null)
                is.close();
            if(os!=null)
                os.close();
        }
   
       
       
    }

}

Ist die Datei kleiner als mein Buffer funktioniert es super :)
Wird die Datei größer und ich muss Sie aufteilen habe ich ein Problem. Nun sagen wir mal die Rechnung wäre Korrekt und ich komme bei einer Datei größe von 1,33 M / 4 K auf 340 Durchläufe.
Doch wie stelle ich fest wie viel noch über ist von der Datei, was ist wenn es gar nicht mehr 1,33 M sind und ich laufe dann 340 mal durch dann knallt es :(
Irgendwie bin ich hierfür einfach zu doof :( "andernfalls lasse ich es einfach ist ja nur mein Privat vergnügen :D"

LG


Last es gut sein bekomme es nicht hin.
 
Zuletzt bearbeitet:
so doch natürlich geht es nicht :D
Java:
package Insel;
import java.io.*;

public class Filesplit {
    public static long loopRuns(double splitLength, int bufferLength) {
        return (long) (splitLength/bufferLength);
    }
    
    public static void main(String...strings ) throws IOException {
        InputStream is = null;
        OutputStream os = null;
        try {
            File f = new File("/home/buch.pdf");
            is = new FileInputStream("/home/buch.pdf");
            byte[] buffer = new byte[4096];
            String outputPath = "/home/a";
            long fileSize = f.length();
            int countName = 0;
            int bufferLength = buffer.length;
            double splitLength = 1.33*1024*1024;        //1.33 M
            long runs = loopRuns(splitLength,bufferLength); //durchlauefe fuer eine 1.33 M grosse Datei
            long count = (int) (fileSize/splitLength);   
            if(f.isFile()) {
                if(f.length() < buffer.length) {
                    os = new FileOutputStream(outputPath);
                    int b = is.read(buffer);
                    while (b != -1) {
                        os.write(buffer, 0, b);
                        b = is.read(buffer);
                    }
                }
                else {
                    while (count > 0) {
                        countName++;
                        String tmp = outputPath + countName;
                        int runsTmp = runs;
                        os = new FileOutputStream(tmp);
                        int b = is.read(buffer);
                        while (runsTmp > 0) {
                            os.write(buffer, 0, b);
                            b = is.read(buffer);
                            runsTmp--;
                        }
                        count--;
                    }
                }
            }
            else if(f.isDirectory()) {
                System.out.println("");
            }
                else throw new Exception("unknow error");
            
        }
        catch(IOException ex) {
            ex.getMessage();
        }
        catch(Exception ex) {
            ex.getMessage();
        }
        finally {
            if(is!=null)
                is.close();
            if(os!=null)
                os.close();
        }
    
        
        
    }

}

Bekomme das nicht hin da ich keine Ahnung habe wie ich abfrage wieviele bytes ich schon habe :( naja
 
Ich dachte eher an so was (ungetestet)
Java:
void copyBytes(InputStream is, OutputStream os, int size) { 
    byte[] buf = new byte[4096]; // 4 KiB Buffer

    int remaining = size;
    int n;
    while (remaining > 0 && (n = is.read(buf, 0, Math.min(buf.length, remaining))) != -1) {
        os.write(buf, 0, n);
        remaining -= n;
    }
}
 
in die variable size kommt jetzt die Angabe wie groß die Datei sein soll.
Jetzt habe ich nur das nächste Problem wie verhält sich den der FileInputStream und FileOutputStream wenn man jetzt x MB von der Datei abzwickt also liest und schreibt. Macht der FileInputStream dann danach weiter?
Denn das ganze bräuchte man ja dann nur noch um eine Schleife zu legen die eben auch so lange läuft wie sie noch Daten hat oder sehe ich das Falsch?
 
Macht der FileInputStream dann danach weiter?
Natürlich, er wird ja nicht geschlossen. Du kannst also in copyBytes in boolean zurückgeben, ob noch Bytes zur Verfügung stehen könnten (return n != -1;) und dann
dann nur noch um eine Schleife zu legen die eben auch so lange läuft wie sie noch Daten hat oder sehe ich das Falsch?
Also irgendetwas wie
Java:
public void split(InputStream is, String basename, String ext, int size) {
    int fileNo = 1;
    FileOutputStream fos;
    do {
        fos = new FileOutputStream(String.format("%s%d%s", basename, fileNo++, ext));
    } while (copyBytes(is, fos, size));
}
 
Danke.
Wo muss ich jetzt welche Streams schließen?
Oder reicht es wenn ein in einer Methode einen Stream öffnet denn in der main zu schließen?

Ich bekomme es aber leider immer noch nicht hin. Irgendwie verstehe ich das alles noch überhaupt nicht :( sry!

LG

so irgendwie habe ich mir das gedacht aber was muss nach der netten Methode copyBytes dann bei read stehen?
Java:
            int b = is.read(buffer);
            while(b!=-1) {
                copyBytes(is,os,size);
                b=is.read(b, off, len)
            }

wie lese ich nun die Datei die größer ist als 1,33 MB weiter ein?
 
Zuletzt bearbeitet:
Die Methode, die den Stream öffnet, sollte ihn auch schließen. Insofern wäre es besser:
Java:
public void split(InputStream is, String basename, int size) throws IOException {
    int fileNo = 1;
    boolean moreBytes;
    do {
        try(FileOutputStream fos = new FileOutputStream(String.format("%s.%d", basename, fileNo++))) {
            moreBytes = copyBytes(is, fos, size);
        }
    } while (moreBytes);
}
und z. B.
Java:
public void splitFile(File file, int size) throws IOException {
    try(FileInputStream fis = new FileInputStream(file)) {
        split(is, file.getAbsolutePath(), size);
    }
}
zu verwenden.
 
public void split(InputStream is, String basename, int size) throws IOException { int fileNo = 1; boolean moreBytes; do { try(FileOutputStream fos = new FileOutputStream(String.format("%s.%d", basename, fileNo++))) { moreBytes = copyBytes(is, fos, size); } } while (moreBytes); }

Bricht der code nicht ab wenn er die Datei die zu Zerteilen ist 1x Zerteilt hat?
Ich verstehe es nicht! sry


Ich verstehe auch immer noch nicht so ganz wie ich nach der Methode Ihm sagen kann lies doch bitte ab Size weiter ;)
Dann würde ich das ganze einfach in eine while(nach size bis dateiende) packen und dem os immer einen neuen Namen mit geben Fertig :)

Java:
            int size = (int) (1.33*1024*1024);
Java:
    public static boolean copyBytes(InputStream is, OutputStream os, int size) throws IOException {
        byte[] buf = new byte[4096]; // 4 KiB Buffer

        int remaining = size;
        int n=0;
        while (remaining > 0 && (n = is.read(buf, 0, Math.min(buf.length, remaining))) != -1) {
            os.write(buf, 0, n);
            remaining -= n;
        }
        is.close();
        os.close();
        return n!=-1;
    }
Denn so in der Richtung geht es nicht:
Java:
int size = (int) (1.33*1024*1024);
            byte[] buffer = new byte[size];
 int b = is.read(buffer);
            while(b != -1) {
                String outputPath2 = outputPath+count;
                os = new FileOutputStream(outputPath2);
                copyBytes(is,os,size);
                count++;
                b = is.read(buffer, 0, b);
            }
 
Zuletzt bearbeitet:
Nochmal langsam:
Java:
boolean copyBytes(InputStream is, OutputStream os, int size) throws IOException { 
    byte[] buf = new byte[4096]; // 4 KiB Buffer

    int remaining = size;
    int n;
    while (remaining > 0 && (n = is.read(buf, 0, Math.min(buf.length, remaining))) != -1) {
        os.write(buf, 0, n);
        remaining -= n;
    }
    return n != -1;
}
Die Methode kopiert bis zu size Bytes aus dem InputStream in den OutputStream. Am Ende gibt es zwei Möglichkeiten: entweder es wurden size Bytes kopiert - dann ist remaining == 0 - oder das Dateiende wurde zuvor erreicht - dann ist n == -1. Wurde das Dateiende noch nicht erreicht (n != 1), dann können weitere Bytes aus dem InputStream gelesen werden. Die Methode liefert false zurück, falls das Dateiende erreicht wurde, true sonst.
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben