Szyfr Vigenere’a

Wstępne informacje znajdziesz tutaj
Szyfr Vigenere’a w wersji klasycznej stosuje 26 znaków alfabetu angielskiego. Do szyfrowania używa się klucza, który jest pewnym słowem złożonym z powyższych liter.
Tworzy się tablicę, która określa zasady podstawień (Rys. 167):


Rys. 168. Zasady podstawień

Szyfrowanie
Szukamy litery wiadomości w rzędzie np. B, szukamy litery klucza w kolumnie np. C i odczytujemy literę zaszyfrowanej wiadomości, w tym przypadku D na przecięciu wiersza z kolumną. Oczywiście znaki mogą być przesuwane np. o dwie litery w każdym wierszu, etc.
Odszyfrowywanie
Odszyfrowywanie przebiega odwrotnie. Wyszukujemy literę wiadomości w kolumnie klucza np. C i odczytujemy literę w wierszu wiadomości.
Jeżeli do tabeli podstawień dodasz inne znaki, szczególnie narodowe, musisz pamiętać żeby do odszyfrowywania wiadomości użyć tej samej tablicy kodowej co nadawca wiadomości. W przypadku wiadomości szyfrowanej ręcznie nie ma to żadnego znaczenia, ale w wiadomości szyfrowanej na komputerze ma istotne znaczenie.

Klasa Vigenere.java
package crypto.vigenere;


public class Vigenere{
	public final static String[] CHARS = {"A", "B", "C", "D", "E", "F", "G",
			"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
			"U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a",
			"b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
			"o", "p", "q", "r", "s"};
	private final static int len = CHARS.length;
	private final static String REGEX = "[^A-s0-9ąćęłńóśżź]";
	private final static int div = 63;
	private String klucz;
	private String wiadomosc;
	private final String kluczKompl;
	private final String wiadomoscZaszyfr;
	private final String[][] tablZnakow;
	private String wiadomoscOdszyfr;

	//Znaki dozwolone: litery łacińskie, litery polskie, cyfry,
	//spacja, przecinek, kropka, średnik, dwukropek, cudzysłów
	//inne znaki zostaną z tekstu usunięte. 
	public Vigenere(String key, String message){
		klucz = key.replace(' ', '[');
		wiadomosc = message.replace(' ', '[');
		klucz = klucz.replace(',', '\\');
		wiadomosc = wiadomosc.replace(',', '\\');
		klucz = klucz.replace('.', ']');
		wiadomosc = wiadomosc.replace('.', ']');
		klucz = klucz.replace(';', '^');
		wiadomosc = wiadomosc.replace(';', '^');
		klucz = klucz.replace(':', '_');
		wiadomosc = wiadomosc.replace(':', '_');
		klucz = klucz.replace('"', '`');
		wiadomosc = wiadomosc.replace('"', '`');
		klucz = oczysc(klucz);
		wiadomosc = oczysc(wiadomosc);
		wiadomosc = wiadomosc.replace('0', 'a');
		klucz = klucz.replace('0', 'a');
		wiadomosc = wiadomosc.replace('1', 'b');
		klucz = klucz.replace('1', 'b');
		wiadomosc = wiadomosc.replace('2', 'c');
		klucz = klucz.replace('2', 'c');
		wiadomosc = wiadomosc.replace('3', 'd');
		klucz = klucz.replace('3', 'd');
		wiadomosc = wiadomosc.replace('4', 'e');
		klucz = klucz.replace('4', 'e');
		wiadomosc = wiadomosc.replace('5', 'f');
		klucz = klucz.replace('5', 'f');
		wiadomosc = wiadomosc.replace('6', 'g');
		klucz = klucz.replace('6', 'g');
		wiadomosc = wiadomosc.replace('7', 'h');
		klucz = klucz.replace('7', 'h');
		wiadomosc = wiadomosc.replace('8', 'i');
		klucz = klucz.replace('8', 'i');
		wiadomosc = wiadomosc.replace('9', 'j');
		klucz = klucz.replace('9', 'j');
		wiadomosc = wiadomosc.replace('Ą', 'k');
		klucz = klucz.replace('Ą', 'k');
		wiadomosc = wiadomosc.replace('Ć', 'l');
		klucz = klucz.replace('Ć', 'l');
		wiadomosc = wiadomosc.replace('Ę', 'm');
		klucz = klucz.replace('Ę', 'm');
		wiadomosc = wiadomosc.replace('Ł', 'n');
		klucz = klucz.replace('Ł', 'n');
		wiadomosc = wiadomosc.replace('Ń', 'o');
		klucz = klucz.replace('Ń', 'o');
		wiadomosc = wiadomosc.replace('Ó', 'p');
		klucz = klucz.replace('Ó', 'p');
		wiadomosc = wiadomosc.replace('Ś', 'q');
		klucz = klucz.replace('Ś', 'q');
		wiadomosc = wiadomosc.replace('Ż', 'r');
		klucz = klucz.replace('Ż', 'r');
		wiadomosc = wiadomosc.replace('Ź', 's');
		klucz = klucz.replace('Ź', 's');
		wiadomosc = oczysc2(wiadomosc);
		klucz = oczysc2(klucz);
		kluczKompl = podstawKlucz(klucz, this.wiadomosc);
		tablZnakow = fillTable();
		wiadomoscZaszyfr = zaszyfruj(tablZnakow, kluczKompl, wiadomosc);
		wiadomoscOdszyfr = odszyfruj(tablZnakow, kluczKompl, wiadomoscZaszyfr);
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('[', ' ');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('\\', ',');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace(']', '.');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('^', ';');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('_', ':');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('`', '"');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('a', '0');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('b', '1');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('c', '2');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('d', '3');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('e', '4');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('f', '5');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('g', '6');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('h', '7');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('i', '8');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('j', '9');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('k', 'Ą');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('l', 'Ć');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('m', 'Ę');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('n', 'Ł');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('o', 'Ń');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('p', 'Ó');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('q', 'Ś');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('r', 'Ż');
		wiadomoscOdszyfr = wiadomoscOdszyfr.replace('s', 'Ź');
	}

	public String getWiadomoscZaszyfr() {
		return wiadomoscZaszyfr;
	}

	public String getWiadomoscOdszyfr() {
		return wiadomoscOdszyfr;
	}

	public String[][] getTablZnakow() {
		return tablZnakow;
	}

	public String getKluczKompl() {
		return kluczKompl;
	}

	public static String oczysc(String toClean) {
		String str = toClean;
		str = str.toUpperCase();
		return str;
	}

	public static String oczysc2(String toClean) {
		String str = toClean;
		str = str.replaceAll(REGEX, "");
		return str;
	}

	public static String podstawKlucz(String key, String message) {
		StringBuilder sb = new StringBuilder();
		int counter = 0;
		for(int i = 0; i < message.length(); i++){
			if(counter == key.length()){
				counter = 0;
			}
			sb.append(key.charAt(counter));
			counter++;
		}
		return sb.toString();
	}

	private static String[][] fillTable() {
		String[][] tablica = new String[len][len];
		int counter = 0;
		for(int i = 0; i < len; i++){
			for(int j = 0; j < len; j++){
				tablica[i][j] = CHARS[(i + counter) % len];
				counter++;
			}
		}
		return tablica;
	}

	public String getKlucz() {
		return klucz;
	}

	public String getWiadomosc() {
		return wiadomosc;
	}

	public static void display(String[][] temp) {
		for(int i = 0; i < temp.length; i++){
			System.out.print((i + 1) + "\t");
			for(int j = 0; j < temp[i].length; j++){
				System.out.print(temp[i][j] + " ");
			}
			System.out.println();
		}
		System.out.println();
	}

	private static String zaszyfruj(String[][] tablica, String key,
			String message) {
		String zaszyfr;
		StringBuilder sb = new StringBuilder();
		for(int i = 0; i < message.length(); i++){
			sb.append(tablica[(message.charAt(i) & div) - 1][(key.charAt(i) & div) - 1]);
		}
		zaszyfr = sb.toString();
		return zaszyfr;
	}

	private static String odszyfruj(String[][] tablica, String key,
			String message) {
		StringBuilder sb = new StringBuilder();
		for(int j = 0; j < message.length(); j++){
			int kol = (key.charAt(j) & div) - 1;
			String str = message.substring(j, j + 1);
			for(int i = 0; i < len; i++){
				String str2 = tablica[i][kol];
				if(str2.equals(str)){
					sb.append(tablica[i][0]);
					break;
				}
			}
		}
		return sb.toString();
	}
}

Wyniki

Klasa Crypto02.java
package crypto.vigenere;

public class Crypto02 {
    public static void main(String[] args) {
        String klu = VigUtil.readFile("crypto/src/crypto/vigenere/assets/klucz1.txt");
        String wiad = VigUtil.readFile("crypto/src/crypto/vigenere/assets/wiadomosc1.txt");
        Vigenere ig = new Vigenere(klu, wiad);
        System.out.println("klucz: " + klu);
        System.out.println("wiadomosc: " + wiad);
        System.out.println("klucz wstepnie prztworzony: " + ig.getKlucz());
        System.out.println("wiadomosc  wstepnie przetworzona: "
                + ig.getWiadomosc());
        System.out.println("klucz kompletnie przetworzony: "
                + ig.getKluczKompl());
        System.out.println("wiadZaszyfr: " + ig.getWiadomoscZaszyfr());
        System.out.println("wiadOdszyfr: " + ig.getWiadomoscOdszyfr());
        VigUtil.writeFile(ig.getWiadomoscZaszyfr(), "crypto/src/crypto/vigenere/assets/zaszyfr1.txt");
        VigUtil.writeFile(ig.getWiadomoscOdszyfr(), "crypto/src/crypto/vigenere/assets/odszyfr1.txt");
    }
}

Po uruchomieniu klasy Crypto02.java otrzymujemy;

 title="Szyfr XOR/Vernama/Vigenere'a"
klucz: Litwo Ojczyzno moja Ty jesteś jak zdrowie. Ile cię trzeba cenić, tylko się
wiadomosc: W Strzebrzeszynie chrząszcz brzmi w trzcinie i Strzebrzeszyn z tego słynie.
klucz wstepnie prztworzony: LITWO[OJCZYZNO[MOJA[TY[JESTEq[JAK[ZDROWIE][ILE[CIm[TRZEBA[CENIl\[TYLKO[SIm
wiadomosc  wstepnie przetworzona: W[STRZEBRZESZYNIE[CHRZkSZCZ[BRZMI[W[TRZCINIE[I[STRZEBRZESZYN[Z[TEGO[SnYNIE]
klucz kompletnie przetworzony: LITWO[OJCZYZNO[MOJA[TY[JESTEq[JAK[ZDROWIE][ILE[CIm[TRZEBA[CENIl\[TYLKO[SImL
wiadZaszyfr: bcfj`ASKTs]lgghUSdCberR\^Um_rlcMSBp^e`pKMjcMfMBU\KAXSk^FSA[RhbSo_Zgf]Is`Qqh
wiadOdszyfr: W STRZEBRZESZYNIE CHRZĄSZCZ BRZMI W TRZCINIE I STRZEBRZESZYN Z TEGO SŁYNIE.

Jak widzimy, wszystko się zgadza, z tym małym wyjątkiem, że wynik jest wyświetlony z użyciem wyłącznie dużych liter, chociaż treść jest poprawna. Przerobienie klasy tak, żeby wyświetlała poprawnie małe i duże litery – pozostawiam Czytelnikowi.

Tablica podstawień

Klasa Crypto03.java
package crypto.vigenere;

public class Crypto03 {
    public static void main(String[] args) {
        String klu = VigUtil.readFile("crypto/src/crypto/vigenere/assets/klucz1.txt");
        String wiad = VigUtil.readFile("crypto/src/crypto/vigenere/assets/wiadomosc1.txt");
        Vigenere ig = new Vigenere(klu, wiad);
        Vigenere.display(ig.getTablZnakow());
    }
}

Po uruchomieniu tej klasy zobaczymy tablicę podstawień (Rys 168):

Tablica podstawień testu Vigener'a
Rys. 168. Tablica podstawień

Klasy uzupełniające

Klasa FrequencyMap.java
package crypto.vigenere;

import java.awt.*;
import java.io.*;
import java.util.*;

/**
 * Klasa oblicza częstość występowania elementu typu T *
 *
 * @param <T> obiekt dowolnej klasy
 */
public class FrequencyMap<T> {
    private TreeMap<T, Integer> map;
    private PrintWriter pw;
    private int elementy;

    public FrequencyMap() {
        map = new TreeMap<>();
    }

    public Point[] getAllPoints() {
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        int size = set.size();
        Point[] points = new Point[size];
        int i = 0;
        for (Map.Entry<T, Integer> entry : set) {
            points[i] = new Point((Integer) entry.getKey(),
                    entry.getValue());
            i++;
        }
        return points;
    }

    /**
     * Dodaje obiekt do kolekcji, a jeśli element już w niej jest
     * dodaje wystąpienie danego elementu.
     *
     * @param obj - dodawany element
     */
    public void add(T obj) {
        Integer count = map.get(obj);
        if (count == null) {
            map.put(obj, 1);
        } else {
            map.put(obj, ++count);
        }
        elementy++;
    }

    /**
     * Usuwa wystąpienie danego elementu z kolekcji. Jeśli pozostał
     * tylko jeden element usuwa go z kolekcji
     *
     * @param obj - usuwany obiekt
     */
    public void remove(T obj) {
        Integer count = map.get(obj);
        if (count != null) {
            if (count == 1) {
                map.remove(obj);
            } else {
                map.put(obj, --count);
            }
        }
        elementy--;
    }

    /**
     * drukuje liczbę wystąpień (częstość) danego obiektu
     * na konsoli
     *
     * @param obj - obiekt, którego liczbę wystąpień chcemy poznać
     */
    public void print(T obj) {
        if (map.containsKey(obj)) {
            Set<Map.Entry<T, Integer>> set = map.entrySet();
            for (Map.Entry<T, Integer> entry : set) {
                if ((entry.getKey()).equals(obj)) {
                    System.out.println(entry.getKey() + " " + entry.getValue());
                    break;
                }
            }
        }
    }

    public long[] getAllAsLongs() {
        Tuple2L[] points = getAllAsTuplesL();
        int sum = 0;
        int counter = 0;
        for (Tuple2L tuple2L : points) {
            sum += tuple2L.getY();
        }
        long[] liczby = new long[sum];
        for (Tuple2L point : points) {
            int licznik = (int) point.getY();
            int liczba = (int) point.getX();
            for (int m = 0; m < licznik; m++) {
                liczby[counter] = liczba;
                counter++;
            }
        }
        return liczby;
    }

    public Tuple2L[] getAllAsTuplesL() {
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        int size = set.size();
        Tuple2L[] points = new Tuple2L[size];
        int i = 0;
        for (Map.Entry<T, Integer> entry : set) {
            points[i] = new Tuple2L((Long) entry.getKey(), entry.getValue());
            i++;
        }
        return points;
    }

    /**
     * drukuje na konsoli obiekty i ich częstości, w takiej kolejności
     * w jakiej są uporządkowane w mapie
     */
    public int[] printAll() {
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        int i = 0;
        int[] tabl = new int[set.size()];
        for (Map.Entry<T, Integer> entry : set) {
            System.out.println(entry.getKey() + " " + entry.getValue());
            tabl[i] = entry.getValue();
            i++;
        }
        return tabl;
    }

    /**
     * drukuje na konsoli obiekty i ich częstości uporządkowane
     * według malejącej częstości.
     */
    public void printAllSorted() {
        ArrayList<Freq<T, Integer>> al = new ArrayList<>();
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        for (Map.Entry<T, Integer> entry : set) {
            al.add(new Freq<>(entry.getKey(), entry.getValue()));
        }
        Collections.sort(al);
        for (Freq<T, Integer> f : al) {
            System.out.println(f);
        }
    }

    /**
     * Drukuje do wskazanego pliku obiekty i ich częstości uporządkowane
     * według malejącej częstości.
     *
     * @param plik - ścieżka do pliku.
     */
    public void printAllSortedToFile(String plik) {
        try {
            pw = new PrintWriter(plik);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        ArrayList<Freq<T, Integer>> al = new ArrayList<>();
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        for (Map.Entry<T, Integer> entry : set) {
            al.add(new Freq<>(entry.getKey(), entry.getValue()));
        }
        Collections.sort(al);
        for (Freq<T, Integer> f : al) {
            pw.println(f);
        }
        pw.close();
    }

    public void setMap(TreeMap<T, Integer> map) {
        this.map = map;
    }

    /**
     * @return liczbę zsumowaną liczbę elementów w kolekcji. W przypadku
     * np. słów podaje łączną ogólną liczbę słów w kolekcji
     */
    public int getElementy() {
        return elementy;
    }

    /**
     * Zwraca częstość występowania obiektu obj. W przypadku słów zwraca
     * liczbę wystąpień danego słowa
     *
     * @param obj - element, kórego częstość chcemy sprawdzić
     * @return - częstość (liczba wystąpień) elementu obj
     */
    public int getFrequency(T obj) {
        int freq = 0;
        if (map.containsKey(obj)) {
            Set<Map.Entry<T, Integer>> set = map.entrySet();
            for (Map.Entry<T, Integer> entry : set) {
                if ((entry.getKey()).equals(obj)) {
                    freq = entry.getValue();
                    break;
                }
            }
        }
        return freq;
    }

    public int razem() {
        int a = 0;
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        for (Map.Entry<T, Integer> entry : set) {
            a += entry.getValue();
        }
        return a;
    }

    /**
     * Podaje wielkość kolekcji. W przypadku słów jest to liczba różnych
     * słów w kolekcji
     *
     * @return - wielkość kolekcji
     */
    public int getSize() {
        return map.size();
    }

    /**
     * Getter
     *
     * @return - zwraca mapę
     */
    public TreeMap<T, Integer> getMap() {
        return map;
    }

    /**
     * Metoda pomocnicza do wczytywania tekstów i podziału na tokeny (słowa)
     *
     * @param file - ścieżka do pliku
     * @return - mapę częstości słów w pliku file
     */
    public static FrequencyMap<String> read(String file) {
        FrequencyMap<String> map = new FrequencyMap<>();
        try {
            try (BufferedReader in = new BufferedReader(
                    new FileReader(new File(file)))) {
                String s;
                while ((s = in.readLine()) != null) {
                    StringTokenizer st = new StringTokenizer(s, " ,;.");
                    while (st.hasMoreTokens()) {
                        map.add(st.nextToken());
                    }
                }
            }
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        }
        return map;
    }

    public void printAll2() {
        Set<Map.Entry<T, Integer>> set = map.entrySet();
        for (Map.Entry<T, Integer> entry : set) {
            System.out.println(entry.getKey() + " " + entry.getValue());
        }
    }
}

/**
 * Klasa pomocnicza pozwalająca na umieszczanie elementów
 * w arrayliście
 */
class Freq<K, V> implements Comparable<Freq<K, V>> {
    private K key;
    private V value;

    public Freq(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "[" + key + ", " + value + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        if (!(obj instanceof Freq<?, ?>)) {
            return false;
        }
        Freq<?, ?> fr = (Freq<?, ?>) obj;
        return key.equals(fr.getKey()) && value.equals(fr.getValue());
    }

    @Override
    public int hashCode() {
        return 17 * key.hashCode() + 19 * value.hashCode();
    }

    @Override
    public int compareTo(Freq<K, V> o) {
        Integer i1 = (Integer) this.getValue();
        Integer i2 = (Integer) o.getValue();
        return i2.compareTo(i1);
    }
}
Klasa Tuple2L.java
package crypto.vigenere;

public class Tuple2L {
    private long x;
    private long y;

    public Tuple2L() {
        this(0L, 0L);
    }

    public Tuple2L(long x, long y) {
        this.x = x;
        this.y = y;
    }

    public double distance(Tuple2L tuple) {
        return Math.sqrt(x * tuple.getX() + y * tuple.getY());
    }

    @Override
    public String toString() {
        return "[" + x + ", " + y + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        if (!(obj instanceof Tuple2L)) {
            return false;
        }
        Tuple2L t2 = (Tuple2L) obj;
        return (x == t2.getX()) && (y == t2.getY());
    }

    @Override
    public int hashCode() {
        return 17 * Long.valueOf(x).hashCode() + 19 * Long.valueOf(y).hashCode();
    }

    public long getX() {
        return x;
    }

    public void setX(long x) {
        this.x = x;
    }

    public long getY() {
        return y;
    }

    public void setY(long y) {
        this.y = y;
    }
}
Klasa VigUtil.java
package crypto.vigenere;

import java.io.*;
import java.nio.*;
import java.nio.charset.*;

public class VigUtil {
    private VigUtil() {
    }

    public static String readFile(String file) {
        StringBuilder buf = new StringBuilder();
        BufferedReader we = null;
        try {
            we = new BufferedReader(new FileReader(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        String linia;
        try {
            while ((linia = we.readLine()) != null) {
                buf.append(linia);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            we.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buf.toString();
    }

    public static String byteArrayToString(byte[] tabl) {
        Charset charset = Charset.forName("UTF-8");
        ByteBuffer buf = ByteBuffer.wrap(tabl);
        CharBuffer cbuf = charset.decode(buf);
        return cbuf.toString();
    }

    public static void writeFile(String wiad, String plik) {
        File file = new File(plik);
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(file));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        try {
            bw.write(wiad);
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *