13. december 2002 - 16:27Der er
55 kommentarer og 2 løsninger
Byte- og binær- skrivning til fil
Jeg har udviklet en algoritme til komprimering af tekst, som er en del af min 3. årsopg i matematik. Desværre har jeg slet ikke styr på java IO og det er ikke så let at sætte sig ind i, som jeg lige havde troet.
Jeg har brug for et program:
Det skal kunne læse og skrive bytes (som skal repræsentere de 255 tegn i ascii) fra og til en fil, der udelukkende er en sekvens af bytes.
Det skal kunne læse og skrive bits fra og til en fil, der udelukkende er en sekvens af bits.
Jeg kender ikke så meget til, hvordan det fungerer i Java, men i Pascal skulle det have været noget med File of Byte og File of Boolean. Desværre er jeg ret forvirret over alle de klasser, der vikles ind i hinanden når man skal skrive til en fil i java, og jeg har slet ikke nogen anelse om hvordan jeg afgør hvilken datatype en fil skal/kan bestå af...
Selve algoritmen jeg bruger har jeg styr på, så jeg skal bare vide hvordan jeg kan skrive og læse fra disse to filer. Bytefilen skal omsættes til en sekvens af bits, som så gemmes i den anden fil (hvortil jeg kan skrive bits).
Vær flink at hjælp mig, så jeg slipper på at bruge tid på dette fremfor at skrive opgaven.
Her er et eksempel som performer langt bedre end ovenstående, fordi der er anvendt buffered input og output stream, hvilket man næsten altid gør klogt i.
Hvis man ikke går bliver der lavet et acces til filen hver eneste gang, hvilket fører til meget dårligt performance.
Du skal samtidigt catche IOException hvis du arbejder med filer, hvilket du også kan se i mit eksempel, som er et helt program som gemmer nogle bytes i en fil, og derefter læser dem ind igen og udskriver dem.
Hvis du leger med eksemplet pas lidt på, jeg har kun reserveret plads til max 10 karakterer ved indlæsning, hvis du gemmer flerer henter den max 10 ind igen.
Man skulle selvfølgelig så f.eks. hente af flere gange osv, men som eksempel er dette fint nok.
Hvis der er problemmer bare spørg.
/* * Binary.java * * Created on 13. december 2002, 17:07 */
import java.io.*;
/** * * @author Disky * @version */ public class Binary {
/** Creates new Binary */ public Binary() { try { byte out[]={1,2,3,4,99}; BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("c:/test.txt")); bos.write(out); bos.write(117); bos.close();
byte ind[]=new byte[10]; //reserver plads nok int længde=0; BufferedInputStream bis=new BufferedInputStream(new FileInputStream("c:/test.txt")); længde=bis.read(ind,0,ind.length); //læs max det antal pladser som der er i ind arrayet. //du kan også bruge bis.read(byte) eller bis.read(b[]) for(int x=0;x<længde;x++) { System.out.println(""+ind[x]); } bis.close();
} catch(IOException e) { e.printStackTrace(); } }
/** * @param args the command line arguments */ public static void main(String args[]) { new Binary(); }
Kan man ikke lave et eller andet med en buffer, så jeg kan skrive bits til denne buffer og den automatisk samler disse i bytes og skriver dem til filen.
Det letteste ville være at loade og save hele filen i et hug.
Ellers skal du til at rode med at hente og gemme dele af bytes af gangen, og det kræver enten at du kun kan gemme i grupper af 8 bit, eller bruger random access filer.
import java.io.*; public class tom { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("hej.dat"); byte[] inp = new byte[1000]; int i = 0; int data; boolean stop = false; while ((data=fis.read())!=-1) { try { inp[i++] = (byte)data; } catch(EOFException e) { } } } }
Eller endnu bedre: import java.io.*; public class tom { public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("hej.dat"); byte[] inp = new byte[1000]; int i = 0; boolean stop = false; try { while (true) { inp[i++] = (byte)fis.read(); } } catch(EOFException e) { break; } } }
Tak for det hele, jeg har overhovedet ikke forladt computeren siden jeg skrev indlægget, men det tager lidt tid for mig at teste det, fordi jeg slet ikke er vant til at skrive java. Anyway: Når den læser et æ så bliver værdien -26!!! Den skal kunne tage alle tegn, så man kan sætte den til at løbe et hvilket som helst txt-dokument igennem. Er det svært at få ordnet?
jeg tog som udgangspunkt at byte er signed 0-255 (vant til fra Pascal), og hvis du ser længere oppe i carsten knudsens indlæg (hvis det da er sandt) så giver det jo netop mening hvis byte ikke er signed for 0.
Lad os sige 67% interesse for Java + 33% interesse for point kombineret med at have brugt hundredevis af timer gennem de sidste par år på at skrive Java kode der læser og skriver records med unsigned 8/16/32 bits integers i.
Nu er jeg stødt på noget, som jeg slet ikke havde tænkt igennem før. Den skal tælle hvor mange gange, de forskellige tegn forekommer, og det kan jeg sagtens lave. Men efterfølgende skal den generere en liste med tegnene arrangeret efter hyppighed. Kan det ikke ordnes på anden måde end brug af array. Jeg kunne godt tænke mig en løsning hvor objekterne rent faktisk skifter plads afhængigt af deres hyppighed. ?
Jeg er java konsulent for et firma i nærheden af København, og har i næsten et år været hos et stort mobil telefon firma, hvor jeg arbejder med Java og C udvikling til mobiltelefoner.
Nu skal i lige se, hvad jeg har lavet !!! jeg er ikke helt dum, selvom det er nærliggende at tro :-) Men det kunne jo sikker have været lavet smartere? (btw. så skal jeg senere have det udvidet til at tælle forekomster af strenge på to tegn).
import java.io.*; public class tom { static Tegn[] fk1 = new Tegn[256]; public static void main(String[] args) throws Exception{ FileInputStream fis = new FileInputStream("hej.dat"); for (int i = 0 ; i < 256 ; i++) fk1[i] = new Tegn(i); byte a = 0; int count = -1; int buffer; while (true) { buffer = fis.read(); if (buffer == -1) break; a = (byte)buffer; System.out.println(a); fk1[a].inc(); }
for (int i = 0; i < 256 ; i++) { fk1[i].hyppighed = (int)(Math.random()*10); }
skriv(); Tegn tmp; int min,max; for ( int l=0,r=255 ; l<r ; l++, r--) { min = r; max = l; for (int i=l+1 ; i<r-1 ; i++) { if ( fk1[i].getH() < fk1[min].getH() ) min = i; else if ( fk1[i].getH() > fk1[max].getH() ) max = i; tmp = fk1[max]; fk1[max] = fk1[l]; fk1[l] = tmp; tmp = fk1[min]; fk1[min] = fk1[r]; fk1[r] = tmp; } } skriv(); }
public static void skriv() { for (int i = 0 ; i<256 ; i++) System.out.print(fk1[i].værdi + ": " + fk1[i].getH() + " "); System.out.println("h2j"); } }
class Tegn { byte værdi; int hyppighed; //antal registrering Tegn(int nr) { værdi = (byte)nr; hyppighed = 0; } public void inc() { hyppighed++; } public int getH() { return hyppighed; } }
hmmm du henter en fil tælle data i den, for derefter at overskrive det talte med dit random kald, øh hvorfor ? Istedet for at lave din egen sortering kan du med fordel bruge den Java har :)
Angående komprimering, så er det et system jeg fandt på tidligere idag. Det eneste jeg har af materiale til min opg som vedrører komprimering er en artikel fra 93 i politiken om huffman, men den omtaler slet ikke, hvordan man får computeren til at holde styr på, hvornår en ny datasekvens begynder. Men så tænkte jeg:
xxxk xxxk xxxk xxxk xxxk .... .... osv.
hvor k hele tiden afgør om den følgende sekvens skal kobles på. Et ofteforekommende tegn f.eks. skulle f.eks. optræde som
yyy0
hvor koden for "e" er yyy. Mens et sjældent forekommende tegn f.eks. § skal repræsenteres af
yyy1yyy1yyy0
hvor yyyyyyyyy er den binære værdi for §
Disse repræsentationer bliver selvfølgeligt mine egne afhængigt af tegnenes frekvenser. De overflødige pladser kan bruges til ofte forekommende strenge af 2 eller 3 tegn.
Jeg ved endnu ikke om jeg vil opdele det netop med xxxk xxxk osv. Det kunne jo også være xxx k xxxxxk hvorved netop asciialfabetet repræsenteres.
Jeg har lige pillet lidt i koden, der var lidt problemmer med at byte er signed, så et tal som 128 blev til -128 og det gav ArrayOutOfBoundException.
Gem værdierne som int intil de skal bruges som byte's. Eller typecast dem til en int og derefter and'e med 0xff får at fjerne fortegnsfejlen:
import java.io.*;
public class tom { static Tegn[] fk1 = new Tegn[256];
public static void main(String[] args) throws Exception { FileInputStream fis = new FileInputStream("c:/DbHandler.java"); for (int i = 0 ; i < 256 ; i++) fk1[i] = new Tegn(i); int count = -1; int buffer; while ( (buffer=fis.read())!=-1) { buffer=buffer&0xff; //ellers balade da byte er signed System.out.println(buffer); fk1[buffer].inc(); } /* for (int i = 0; i < 256 ; i++) { fk1[i].hyppighed = (int)(Math.random()*10); } */ skriv(); Tegn tmp; int min,max; for ( int l=0,r=255 ; l<r ; l++, r--) { min = r; max = l; for (int i=l+1 ; i<r-1 ; i++) { if ( fk1[i].getH() < fk1[min].getH() ) min = i; else if ( fk1[i].getH() > fk1[max].getH() ) max = i; tmp = fk1[max]; fk1[max] = fk1[l]; fk1[l] = tmp; tmp = fk1[min]; fk1[min] = fk1[r]; fk1[r] = tmp; } } skriv(); }
public static void skriv() { for (int i = 0 ; i<fk1.length ; i++) { System.out.print(fk1[i].værdi + ": " + fk1[i].getH() + " "); if(i%16==0) System.out.println(""); //få det til at se lidt pænere ud }
System.out.println("h2j"); } }
class Tegn { int værdi; int hyppighed; //antal registrering Tegn(int nr) { værdi = nr; hyppighed = 0; } public void inc() { hyppighed++; } public int getH() { return hyppighed; } }
Pis, for det første skulle random have være udkommenteret, men nu har jeg fundet ud af at den nogen gange kludrer i det, selvom jeg troede jeg havde testet det ordentligt... Nå anyway, hvordan bruger jeg Javas sortering? :-)
En lidt speciel del er denne her: Comparator comp=new Comparator() { public int compare(Object o1, Object o2) { int s1=((Tegn)o1).værdi; int s2=((Tegn)o2).værdi; return s1-s2; } };
Her definerer man en comparator, som finder ud af hvilken af to Tegn der er størst. Man modtager to objekter, dem caster jeg til Tegn, hvorefter jeg henter 'værdi' fra dem. Hvis den første værdi er mindst skal retur værdien være negativ, nul hvis de er ens, og positiv hvis nummer 2 er mindst.
import java.io.*; import java.util.*;
public class tom { Tegn[] fk1 = new Tegn[256];
public static void main(String[] args) throws Exception { new tom(); }
tom() throws Exception { FileInputStream fis = new FileInputStream("c:/DbHandler.java"); for (int i = 0 ; i < 256 ; i++) fk1[i] = new Tegn(i); int count = -1; int buffer; while ( (buffer=fis.read())!=-1) { buffer=buffer&0xff; //ellers balade da byte er signed System.out.println(buffer); fk1[buffer].inc(); } /* for (int i = 0; i < 256 ; i++) { fk1[i].hyppighed = (int)(Math.random()*10); } */ skriv(); Tegn tmp;
Comparator comp=new Comparator() { public int compare(Object o1, Object o2) { int s1=((Tegn)o1).værdi; int s2=((Tegn)o2).værdi; return s1-s2; } };
java.util.Arrays.sort(fk1,comp);
skriv(); }
public void skriv() { for (int i = 0 ; i<fk1.length ; i++) { System.out.print(fk1[i].værdi + ": " + fk1[i].getH() + " "); if(i%16==0) System.out.println(""); //få det til at se lidt pænere ud }
System.out.println("h2j"); } }
class Tegn { int værdi; int hyppighed; //antal registrering Tegn(int nr) { værdi = nr; hyppighed = 0; } public void inc() { hyppighed++; } public int getH() { return hyppighed; } }
public class Tom { static ArrayList fk1 = new ArrayList(); public static void main(String[] args) throws Exception { BufferedInputStream bis = new BufferedInputStream(new FileInputStream("hej.dat")); for (int i = 0; i < 256; i++) { fk1.add(new Tegn(i)); } int buffer; while ((buffer = bis.read()) > 0) { System.out.println(buffer); Tegn t = (Tegn) fk1.get(buffer); t.inc(); } for (int i = 0; i < 256; i++) { Tegn t = (Tegn) fk1.get(i); t.inc((int) (Math.random() * 10)); } skriv(); Collections.sort(fk1); skriv(); }
public static void skriv() { for (int i = 0; i < 256; i++) { Tegn t = (Tegn) fk1.get(i); System.out.print(t.getV() + ": " + t.getH() + " "); } System.out.println("h2j"); } }
class Tegn implements Comparable { byte værdi; int hyppighed; Tegn(int nr) { værdi = (byte) nr; hyppighed = 0; } public void inc() { hyppighed++; } public void inc(int n) { hyppighed += n; } public int getV() { return værdi; } public int getH() { return hyppighed; } public int compareTo(Object o) { return værdi - ((Tegn) o).værdi; } }
Jeg markerede begge og trykkede accepter, jeg håber I fik 100 hver. Tak for det hele. Men jeg kommer jo nok med endnu en tråd, når jeg er strandet igen :-) VH Velle
Huffman Encoding: Journal of Algorithms 6, pp 163-180 (den er skervet af Knuth hvilket garanter at indholdet er pinligt korrekt - og at den er svær at læse).
Arithmetisk: CACM june 1987 Volume 30 Number 6 (og den er mere menneskelig at læse).
1) Hvis du skal have noget matematik ind, så er pointen den at: for enkelt byte compression algoritmer kræver optimail compression at alle bogstaver encodes med et antal bit svarende til 2-tals-logaritmen af deres andel Aritmetisk rammer lige præcis, Huffman rammer nærmeste heltal. Din algoritme (som beskrevet) vil ramme nærmste 4/8/12.
2) Både aritmetisk og Huffman opererer med 257 tegn (256 værdier + en EOF marker). Det er nødvendigt, fordi eller kan der komme ekstra tegn ind under dekryptering til sidst. Din algoritme (som beskrevet) skulle ikke have det behov, men hvis du forbedrer den, så vil du også få brug for det.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.