Obsah 9. přednášky: Souborový V/V - pokračování Proudový vstup a výstup Textové vs. binární soubory Zpracování textových souborů Příklad použití txt souborů – součin matic Zpracování binárních souborů* Shrnutí Motivační příklad - řešení Tato tematika je zpracována v Záznamy přednášek: str. 167 – 194
Přednášky KIV/PPA1, A. Netrvalová, 2011
9. přednáška
Souborový vstup a výstup Poznámka Široké téma – výklad pouze v rozsahu pro použití v 1. ročníku (více OOP, JXT, PT, ...) Práce se soubory – třída File (viz minulá přednáška)
Proudový vstup a výstup Java: V/V – pojem proud dat (stream) -
vlastnost proudu dat - spojitý tok vpřed zpracování proudu stejné, bez ohledu na směr dat vstup (zdroj) / výstup (cíl) a druh fyzického zařízení (konzola, klávesnice, soubor na disku, síť, ...), rozlišení při inicializaci
Input Stream
Program
Soubor
Output Stream
Strana 2 (celkem 30)
Proudový V/V s využitím prostředků OS -
vstup prostřednictvím System.in (klávesnice) výstup prostřednictvím System.out (obrazovka) přesměrování V/V (již známe) - příkazová řádka
java Xyz < vstup.txt presmerovani vstupu java Xyz > vystup.txt presmerovani vystupu java Xyz < vstup.txt > vystup.txt vstup i vystup
Proudový V/V s využitím knihoven Javy - přesměrování programové Přesměrování vstupu -
prostřednictvím přetížených konstruktorů třídy Scanner stanovením zdroje (klávesnice, soubor) // klavesnice Scanner sc = new Scanner(System.in); // soubor Scanner sc = new Scanner(new File("vstup.txt"));
Strana 3 (celkem 30)
Příklad přesměrování vstupu import java.util.*; import java.io.*; public class PresmerovaniScanner{ public static void main(String [] args) throws Exception { Scanner sc = new Scanner(new File("vstup.txt")); System.out.print("Zadej pocet cisel:"); int pocet = sc.nextInt(); int pole[] = new int[pocet]; System.out.println("Zadavej cisla"); for(int i=0; i<pocet; i++){ pole[i]= sc.nextInt(); } int suma = 0; for(int i=0; i<pole.length; i++){ suma += pole[i]; } double prumer = (double) suma/pocet; System.out.println("Prumer = " + prumer); } }
Zadej pocet cisel:Zadavej cisla Prumer = 3.0 Nadbytečné výpisy vstupu možno odstranit: - metoda přidělující vstup ze souboru (pokud existuje) nebo z klávesnice a ošetřující výjimky Strana 4 (celkem 30)
Metoda nastavZdrojVstupu zároveň ošetří i výjimky import java.util.*; import java.io.*; public class PresmerovaniScannerVolba { static final String JMENO = "vstup.txt"; static Scanner sc; static boolean nastavZdrojVstupu () { boolean klavesnice = true; try { File f = new File(JMENO); if (f.exists()){ sc = new Scanner(f); // ze souboru klavesnice = false; } else { sc = new Scanner(System.in); klavesnice = true; // z klavesnice } } catch (Exception e){ e.printStackTrace(); } return klavesnice; }
Strana 5 (celkem 30)
public static void main(String [] args){ boolean klavesnice = nastavZdrojVstupu(); if(klavesnice){ System.out.print("Zadej pocet cisel:"); } int pocet = sc.nextInt(); int pole[] = new int[pocet]; if(klavesnice){ System.out.println("Zadavej cisla"); } for(int i=0; i<pocet; i++){ pole[i]= sc.nextInt(); } int suma = 0; for(int i=0; i<pole.length; i++){ suma += pole[i]; } double prumer = (double) suma/pocet; System.out.println("Prumer = " + prumer); } }
Poznámka Pokud čteme data ze souboru a neznáme jeho velikost, tj. počet dat, je možno použít metody (typu boolean) - hasNext() - je na vstupu další řetězec? - hasNextInt() - je na vstupu další celé číslo? - hasNextDouble() - je na vstupu další reálné číslo? - hasNextLine() - je na vstupu další řádka? Strana 6 (celkem 30)
Příklad použití metody boolean hasNextInt(): Aritmetický průměr čísel uložených v souboru vstup.txt import java.util.*; import java.io.*; public class IlustraceHasNextInt{ public static void main(String [] args) throws Exception { Scanner sc = new Scanner(new File("vstup.txt")); int suma =0; int pocet = 0; while (sc.hasNextInt()) { suma += sc.nextInt(); pocet++; } double prumer = (double) suma/pocet; System.out.println("Prumer = " + prumer); } } //pro data v souboru: 1 2 3 4 5 6 7 8 9 10
Prumer = 5.5
Přesměrování výstupu
- trochu složitější, nepoužijeme známou konstrukci System.out, ale jinou - předdefinovaný objekt typu PrintStream a již známou metodu println(), a jelikož je to práce se souborem, je nutno soubor zavřít metodou close() Strana 7 (celkem 30)
public class PresmerovaniVystupu { static boolean obrazovka = false; public static void main(String [] args) throws Exception { Scanner sc = new Scanner(new File("vstup.txt")); int pocet = sc.nextInt(); int pole[] = new int[pocet]; for(int i=0; i<pocet; i++){ pole[i]= sc.nextInt(); } int suma = 0; for(int i=0; i<pole.length; i++){ suma += pole[i]; } double prumer = (double) suma/pocet; PrintStream p; if (obrazovka){ p = System.out; } else { p = new PrintStream("vystup.txt"); } p.println("Prumer = " + prumer); p.close(); //uzavreni vystupniho souboru } } V aktuálním adresáři se vytvoří soubor vystup.txt. Strana 8 (celkem 30)
Textové vs. binární soubory Textové soubory -
jsou nám bližší, připomínají V/V na konzolu přípona .txt + jiné - dle specializace (xml, csv) organizace po řádcích obecně různé délky ukončení řádky závislé na platformě
Výhody: úprava textovým editorem, srozumitelnost Nevýhody: pomalejší zpracování, větší délka souboru oproti binárnímu souboru
Binární soubory -
četnější (obrázky, hudba, filmy, spread sheet) vnitřní organizace závisí na daném formátu (.jpg, .mp3, .avi)
Výhody: paměťově úspornější, rychlejší zpracování Nevýhody: speciální prohlížeč formátu, nečitelnost
Poznámka Dále se budeme zabývat pouze proudově orientovanými postupy zpracování souborů.
Java – zpracování souborů -
soubory je nutno otevírat a zavírat odlišné zpracování txt a binárních souborů nutnost ošetření výjimek
Strana 9 (celkem 30)
Zpracování textových souborů Poznámka – načítání se provádí po bytech, které se v implicitním kódování (OS) převádějí na Unicode znaky (více v OOP, JXT)
Primitivní způsob Čtení - třída FileReader -
instance třídy (otevření souboru) – 2 možnosti: FileReader fr = new FileReader("cti.txt"); FileReader fr = new FileReader(new File("cti.txt"));
-
čtení jednotlivých znaků int read() čtení pole znaků int read(char [] pole) – nepoužívat test konce souboru – návratová hodnota -1 int precteno; while((precteno = fr.read()) != -1){ System.out.print((char)precteno); }
-
uzavření souboru close() ošetření výjimky IOException
Příklad Čtení souboru po znacích a výpis na obrazovku, dokud se neobjeví znak '.' Poznámka Tento postup je nevhodný pro formátovaný vstup čísel Strana 10 (celkem 30)
import java.io.*; public class CteniSouboruPoZnacich {
}
public static void main (String [] args){ try { FileReader fr = new FileReader("cti.txt"); int nacti; while ((nacti = fr.read()) != '.'){ System.out.print((char) nacti); } System.out.println(); fr.close(); } catch (IOException e){ e.printStackTrace(); System.exit(1); Ahoj, } jak se mas? } Ja se mam dobre
Zápis - třída FileWriter -
instance třídy (otevření souboru) – 2 možnosti: FileWriter fw = new FileWriter ("zapis.txt"); FileWriter fw = new FileWriter (new File("zapis.txt"));
-
zápis jednotlivých znaků void write(int c) zápis řetězce void write (String s) nutnost odřádkování uzavření souboru close() ošetření výjimky IOException Strana 11 (celkem 30)
Příklad zápisu znaku a řetězce do souboru import java.io.*; public class ZapisDoSouboruZnaky {
}
public static void main (String [] args){ try { FileWriter fw = new FileWriter("zapis.txt"); char znak = 'A'; fw.write(znak); fw.write("hoj,\n"); // win editor – nezobrazi ok String s = "jak se mas?\r\n"; // ok fw.write(s); fw.close(); } catch (IOException e){ e.printStackTrace(); System.exit(1); } Ahoj, } jak se mas?
Řešení vhodné i pro zápis čísel - viz dále Zavedeme nadstavbu – doplňkové třídy tzv. filtry, v konstruktorech tvoří objekty tříd
bufferování třídy BufferedReader, BufferedWriter -
urychlení zpracování užitím vyrovnávací paměti
výstupní formátování - print(), format() Strana 12 (celkem 30)
BufferedReader -
umožňuje čtení po řádcích, znaky konce řádků automaticky ořezává metoda String readLine() - konec souboru null rychlejší zpracování než pomocí Scanner
BufferedWriter konec řádky dle platformy! void newLine()
Příklad – čtení souboru celých čísel a zápis jejich mocnin public static void main (String [] args){ try { Scanner sc = new Scanner(new File("cisla.txt")); PrintWriter pw = new PrintWriter( new BufferedWriter( new FileWriter("mocniny.txt"))); while(sc.hasNextInt()){ int cislo = sc.nextInt(); pw.print(cislo*cislo + " "); } pw.println(); sc.close(); pw.close(); } catch (IOException e){ e.printStackTrace(); // vstup soubor 1 2 3 4 5 6 System.exit(1); 1 4 9 16 25 36 }}}
Strana 13 (celkem 30)
Příklad – tentýž, rychlejší zpracování (bez Scanneru) import java.io.*; import java.util.*; public class CteniAZapisCiselRychle {
}
public static void main (String [] args){ try { BufferedReader bfr = new BufferedReader( new FileReader("cisla.txt")); PrintWriter pw = new PrintWriter( new BufferedWriter( new FileWriter("mocniny.txt"))); String radka; while((radka = bfr.readLine())!= null){ String []cisla = radka.split(" "); // rozdělení na podretezce for(int i=0; i
Vzhledem k tomu, že čísla jsou oddělena mezerou, je nutno je získat rozdělením původního řetězce radka na podřetězce – metoda split() metoda split() funguje pouze při oddělení jednou mezerou! Upozornění:
Podřetězce je pak nutno ještě převést (parsování) na čísla metoda Integer.parseInt()
Příklad – tentýž, pro čísla double, budeme počítat odmocniny a navíc vyřešíme chybu ve vstupním souboru, kde je u jednoho z čísel jako desetinný oddělovač čárka (. vs. ,). Uvedena je pouze úprava sekvence kódu. String radka; while((radka = bfr.readLine())!= null){ radka = radka.replace(',', '.'); //uprava String [] cisla = radka.split(" "); for(int i=0; i
Podrobnější informace (pro zájemce) – Záznamy přednášek.
Strana 15 (celkem 30)
Příklad použití txt souborů – součin matic Vygenerujte náhodně dvě celočíselné matice řádu n (řád je zadán z klávesnice) a uložte matice do textových souborů matA.txt a matB.txt. Z textových souborů přečtěte matici A a matici B, proveďte součin AxB, výsledek - matici C uložte do textového souboru matC.txt. Proveďte rovněž výpis matic A, B, a C na obrazovku.
import java.io.*; import java.util.*; public class TextFilesExample { static void writeMatrix(String nazev, int[][] matrix) { System.out.println(nazev); for (int i = 0; i < matrix.length; i++) { for (int j = 0; j < matrix[i].length; j++) { System.out.format("%3d ", matrix[i][j]); } System.out.println(); } } static int [][] soucinMatic(int[][] a, int [][] b){ int [][] c = new int[a.length][b[0].length]; for (int i = 0; i < a.length; i++) { for (int j = 0; j < b[i].length; j++) { int s=0; for (int k = 0; k < a[i].length ; k++){ s += a[i][k] * b[k][j]; } c[i][j] = s; } } return c; } Strana 16 (celkem 30)
static void writeMatrixToFile (String jmeno, int[][] matrix) { try { final String SOUBOR_OUT = "src\\P09priklady\\example\\" + jmeno; PrintWriter pw = new PrintWriter( new BufferedWriter( new FileWriter(SOUBOR_OUT))); System.out.print(" File writting - please wait ... "); for(int i=0; i<matrix.length; i++) { for(int j=0; j<matrix[i].length; j++){ pw.print(matrix[i][j] + " "); } pw.println(); } System.out.println(" done."); pw.close(); } catch (IOException e) { System.out.println(jmeno +": Write-File Error."); System.exit(1); } } static int[][] loadRandomMatrix (int n) { int [][] mat = new int[n][n]; Random r = new Random(); for (int i=0; i<mat.length; i++){ for (int j=0; j<mat[i].length; j++){ mat[i][j] = r.nextInt(10); } } return mat; } Strana 17 (celkem 30)
static int[][] readMatrixFromFile (String jmeno, int n) { try { final String SOUBOR_IN = "src\\P09priklady\\example\\" + jmeno;
BufferedReader bfr = new BufferedReader( new FileReader(SOUBOR_IN)); int[][] mat = new int[n][n]; System.out.print(" File reading - please wait ..."); String radka; int i=0; while((radka = bfr.readLine())!= null){ String radkaMat[] = radka.split(" "); for(int j=0; j
Strana 18 (celkem 30)
public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.print("Zadej rad matice: "); int n = sc.nextInt(); int [][] matrix = new int[n][n]; matrix = loadRandomMatrix (matrix.length); writeMatrixToFile ("matA.txt", matrix); matrix = loadRandomMatrix (matrix.length); writeMatrixToFile ("matB.txt", matrix); int [][] a = new int[n][n]; int [][] b = new int[n][n]; if(a = readMatrixFromFile ("matA.txt", n) == null) System.exit(1); if((b = readMatrixFromFile ("matB.txt", n)) == null) System.exit(1); writeMatrix("A", a); writeMatrix("B", b); int [][] c = new int[n][n]; c = soucinMatic(a, b); writeMatrix("C", c); writeMatrixToFile ("matC.txt", c); } }
Strana 19 (celkem 30)
Zadej rad matice: 3 File writting - please wait ... done. File writting - please wait ... done. File reading - please wait ... done. File reading - please wait ... done. A 1 7 1 2 8 4 1 8 3 B 2 8 5 1 6 4 1 3 5 C 10 53 38 16 76 62 13 65 52 File writting - please wait ... done.
Pokud by byl zakomentován žlutě označený řádek (viz výše), nevytvoří se soubor matA.txt a následně vznikne chyba při pokusu o jeho čtení: Zadej rad matice: 3 File writting - please wait ... done. File reading - please wait ... matA.txt: Read-file Error. Strana 20 (celkem 30)
Zpracování binárních souborů* pro zájemce (doporučeno oboru Informatika) -
načítají se byty, bez konverze omezíme se pouze na primitivní typy (ne objekty) data jsou v pořadí big-endian
Základní třídy Čtení - FileInputStream -
instance (otevření souboru) – 2 způsoby
FileInputStream fis = new FileInputStream("inData.bin"); FileInputStream fis = new FileInputStream ( new File("inData.bin")); -
čtení byty int read() čtení pole bytů int read(byte [] pole) uzavření souboru close() ošetření výjimky IOException
Zápis - FileOutputStream -
instance třídy (otevření souboru) – 2 možnosti:
FileOutputStream fos = new FileOutputStream ("outData.bin"); FileOutputStream fos = new FileOutputStream ( new File("outData.bin")); -
zápis jednotlivých bytů void write(int b) uzavření souboru close() ošetření výjimky IOException Strana 21 (celkem 30)
Třídy vlastností – bufferování BufferedInputStream BufferedOutputStream Důležité třídy DataInputStream metody - int readInt() - double readDouble() - test konce – EOFException DataOutputStream metody - void writeInt(int i) - void writeDouble (double d)
Příklad – zápis a opětovné načtení binárních dat import java.io.*; public class RWData { public static void main(String [] args) throws IOException { DataOutputStream dataOut; DataInputStream dataIn; int i =10; double d = 1234.56; boolean b = true; try{ // zapis dat dataOut = new DataOutputStream( new FileOutputStream("outData.bin")); } catch (IOException e){ System.out.println("Soubor nelze otevrit!"); return; } Strana 22 (celkem 30)
try { System.out.println("zapis " + i); dataOut.writeInt(i); System.out.println("zapis " + d); dataOut.writeDouble(d); System.out.println("zapis " + b); dataOut.writeBoolean(b); System.out.println("zapis " + 12.2 * 7.4); dataOut.writeDouble(12.2 * 7.4); } catch(IOException e){ System.out.println("Chyba pri zapisu."); } dataOut.close(); System.out.println(); // nasleduje cteni dat try { dataIn = new DataInputStream( new FileInputStream("outData.bin")); } catch (IOException e){ System.out.println("Soubor nelze otevrit!"); return; } try { i = dataIn.readInt(); System.out.println("cteni " + i); d = dataIn.readDouble(); zapis 10 System.out.println("cteni " + d); zapis 1234.56 b = dataIn.readBoolean(); zapis true System.out.println("cteni " + b); zapis 90.28 d = dataIn.readDouble(); System.out.println("cteni " + d); cteni 10 } cteni 1234.56 catch (IOException e){ cteni true System.out.println("Chyba pri cteni."); cteni 90.28 } dataIn.close(); } } Strana 23 (celkem 30)
Shrnutí úroveň abstraktní fyzická logická (vlastnosti, filtry)
úroveň abstraktní fyzická logická (vlastnosti, filtry)
Txt (char) Bin (byte) Reader InputStream FileReader FileInputStream BufferedReader BufferedInputStream Txt (char) Bin (byte) Writer OutputStream FileWriter FileOutputStream BufferedWriter BufferedOutputStream
Textové soubory FileReader
BufferedReader
txt soubor
ReadLine()
txt soubor
pw.println()
FileWriter
BufferedWriter
PrintWriter
Strana 24 (celkem 30)
Motivační příklad (předváděný na předchozí přednášce) Zadání Výsledky rozsáhlého měření, ve kterém byla zkoumána kvalita přenosu harmonického signálu, jsou zapsány v jednotlivých souborech archivu mereni.zip. Část měření byla ovlivněna poruchami, takže jen v několika souborech se nachází nezkreslený přijatý signál, který představuje sinusovku. Amplitudy signálu jsou velmi různorodé, stejně jako počet přijatých vzorků, tj. hodnot v souboru. Napište program v Javě, který pomůže naměřená data v souborech předzpracovat, tj. vypsat do souboru relevantni-vysledky.txt jména těch souborů, které obsahují nezkreslenou sinusovku. Postupujte tak, že program vždy přečte jeden soubor, vykreslí jeho hodnoty v přepočteném měřítku na obrazovku a nechá uživatele vizuálně rozhodnout, zda naměřená data představují sinusovku či nikoliv. Uživatel jednoduchým potvrzením na klávesnici rozhodne o platnosti či neplatnosti dat a současně dá pokyn ke čtení dalšího souboru. Vstup: adresář mereni s textovými soubory s naměřenými daty Výstup: soubor relevantni-vysledky.txt, zobrazení dat grafem Strana 25 (celkem 30)
Strana 26 (celkem 30)
package P08priklady.prikladPredzpracovaniDat; /** Vysledky mereni: v souborech mereni001.txt až merenixyz.txt. */
import java.io.*; import java.util.*; import ppa1.*; /** * @author besoft (doc. Ing. Josef Kohout, PhD) */
public class PredzpracovanaMereni { private static final int cx = 800; private static final int cy = 600; // TODO: zmenit tak, aby ukazovalo na prislusny vstupni soubor // takto na urovni src
private static final String VYSTUPNI_SOUBOR = "data\\relevantni-vysledky.txt"; private static final String VSTUPNI_ADRESAR = "data\\mereni\\"; /** * precte pole dvojic cisel ze zadaneho souboru * hodi vyjimku, pokud soubor neexistuje, nebo pri cteni doslo k chybe * @param pathname jmeno souboru * @return pole celych cisel * @throws IOException */
private static double[][] readData(File f) throws IOException { //nejprve zjistit, kolik tam mame radek
FileReader fr = new FileReader(f); BufferedReader r = new BufferedReader(fr);
Strana 27 (celkem 30)
int n = 0; while (null != r.readLine()){ n++; } r.close(); //a ted precist data
fr = new FileReader(f); Scanner sc = new Scanner(fr); sc.useLocale(Locale.US);
//desetina tecka
double[][] pole = new double[n][2]; //alokace prostoru for (int i = 0; i < n; i++) { pole[i][0] = sc.nextDouble(); pole[i][1] = sc.nextDouble(); } fr.close(); return pole; } /** * @param pole data * @return minimum v poli */
private static double getMin(double[][] pole, int souradnice) { double min = pole[0][souradnice]; for (int i = 1; i < pole.length; i++) { if (pole[i][souradnice] < min) min = pole[i][souradnice]; } return min; } /** * @param pole data * @return maximum v poli */ Strana 28 (celkem 30)
private static double getMax(double[][] pole, int souradnice) { double max = pole[0][souradnice]; for (int i = 1; i < pole.length; i++) { if (pole[i][souradnice] > max) max = pole[i][souradnice]; } return max; } /** * @param dt kreslici zarizeni * @param pole pole hodnot k vykresleni */
private static void plotSimpleGraph(DrawingTool dt, int cx, int cy, double[][] pole) { //zjisti zakladni informace o datech
double minX = getMin(pole, 0); double maxX = getMax(pole, 0); double minY = getMin(pole, 1); double maxY = getMax(pole, 1); //potrebujeme vykreslit plochu [minX, maxX] x [minY, maxY] do //oblasti [0, cx] x [0, cy] => budeme potrebovat body prevzorkovat
double scaleX = maxX == minX ? 1.0 : cx / ((double)(maxX - minX)); double scaleY = maxY == minY ? 1.0 : cy / ((double)(maxY - minY)); int lastX = (int)(scaleY*(pole[0][0] - minX)); int lastY = cy - (int)(scaleY*(pole[0][1] - minY)); for (int i = 1; i < pole.length; i++) { int x = (int)(scaleX*(pole[i][0] - minX)); int y = cy - (int)(scaleY*(pole[i][1] - minY)); if (x != lastX || y != lastY) { dt.line(lastX, lastY, x, y); lastX = x; lastY = y; } } //jeste vykreslit, pro jistotu, posledni bod
dt.line(lastX, lastY, lastX, lastY); } Strana 29 (celkem 30)
/** * entry point * @param args */
public static void main(String[] args) { //vytvor kreslici plochu
DrawingTool dt = new DrawingTool(cx, cy); Scanner sc = new Scanner(System.in); try { FileWriter wr = new FileWriter(VYSTUPNI_SOUBOR); int cislo = 1; while (true) { String filename = String.format("mereni%03d.txt", cislo); File f = new File(VSTUPNI_ADRESAR + filename); if (!f.exists()) { break; } //zadny dalsi soubor neexistuje System.out.println("Nacitam soubor " + filename + "."); //nacti data
double[][] pole = readData(f); // System.out.println("Zobrazuji '" + filename + "'."); // zobrazujeme data
plotSimpleGraph(dt, cx, cy, pole); System.out.print("Je signal v poradku? [A/N]: "); char c = (char)sc.next().charAt(0); sc.nextLine(); if (c == 'a' || c == 'A') { wr.write(filename + "\n"); } System.out.println(); dt.clear(); cislo++; } wr.close(); System.out.println("Done."); } catch (IOException e) { System.out.println("Chyba pri zapisu vysledku."); } mereni023.txt } mereni031.txt } // soubor relevantni-vysledky: mereni052.txt Strana 30 (celkem 30) mereni059.txt