Sortowanie alfabetycznie wg polskich liter
import java.util.*;
import java.io.*;
import java.text.*;
public class sort {
public static void main(String args[]) {
String slowa[] = { "Słowa", "żłobek", "żółw", "Żółć",
"Sznurek", "żar", "Świstak", "samochód", "Pies", "sąd"};
Locale.setDefault(new Locale("pl","PL"));
PrintWriter ekran=null;
try {
ekran=new PrintWriter(new OutputStreamWriter(System.out,"CP852"),true);
} catch(UnsupportedEncodingException e) {
System.out.println("Nie można zainicjować strony kodowej CP852");
ekran=new PrintWriter(new OutputStreamWriter(System.out),true);
}
ekran.println("Tradycyjnie:");
Arrays.sort(slowa);
for (int i=0; i<slowa.length; i++) {
ekran.println(slowa[i] + " ");
}
ekran.println("\nPoprawnie:");
Arrays.sort(slowa, Collator.getInstance());
for (int i=0; i<slowa.length; i++) {
ekran.println(slowa[i] + " ");
}
}
}
W tablicy slowa[] umieśćmy sobie kilka wyrazów zawierających polskie znaki. Będziemy je sortować. Od razu możemy ustawić sobie odpowiedni język, w którym odbędzie się sortowanie. Odpowiada za to obiekt Locale, dla którego tworzymy język polski: "pl" i "PL" zgodnie ze standardami.
Dalej inicjowany jest "polski" ekran, aby pojawiły się na nim napisy w naszym języku, a nie krzaczki - sposób opisany jest w oddzielnej poradzie, więc nie ma sensu się nad nim rozwodzić.
Aby posortować tablicę z wyrazami korzystamy z metody sort(). Pierwsze wywołanie tej metody posortuje nam znaki, ale nie będą one poprawnie posortowane:
Dzieje się tak, ponieważ program nie wie według reguł jakiego języka ma sortować wyrazy (traktuje je jako wyrazy angielskie) i gubi się tam, gdzie występują znaki polskie.
Na szczęście w metodzie sort() można określić w drugim parametrze sposób sortowania. Wywołanie metody Collator.getInstance() spowoduje pobranie ustawień lokalnych (dlatego ustawiliśmy wcześniej Locale dla języka polskiego). I teraz sortowanie odbędzie się zgodnie z naszą gramatyką:
W ten sposób można zdefiniować sortowanie według dowolnego języka. Ponieważ w Javie można skorzystać ze stałych dla kilku języków (ale nie dla polskiego), zamiast deklarować Locale można wprost w metodzie sortującej podać język. Dla języka francuskiego byłoby to:
Arrays.sort(slowa, Collator.getInstance(Locale.FRENCH));
Jak usunąć z katalogu pliki zaczynające się na określoną nazwę, np. "pli*"?
PROBLEM
Chcesz usunąć z wybranego katalogu pliki zaczynające się na wskazaną nazwę.
ROZWIĄZANIE
Napiszę prosty filtr, który pobierze nazwy plików i katalogów zaczynających się określoną nazwą, np. "pli", aby je potem wykasować. Nazwa ta pasuje do plik1.txt, pliki.zip, plik_wazny.doc. Usunięty zostanie każdy plik (katalogi pominę), którego początkowa nazwa pasuje do podanego ciągu:
import java.io.File;
import java.io.FilenameFilter;
import java.lang.*;
public class test {
public static void main (String args[]) {
String sciezka = "./"; // katalog aktualny
String nazwa = "pli"; // początek nazwy plików do usunięcia
File katalog = new File(sciezka);
String[] pliki = katalog.list(new Filtr(nazwa));
if (pliki.length==0) {
System.out.print("Brak podanych plików");
return;
}
File plik;
for (int i=0; i<pliki.length; i++) {
plik = new File(sciezka + pliki[i]);
if (plik.isFile()) { // usuwam tylko pliki, nie katalogi
if (plik.delete()) System.out.println(plik + " usunięty");
}
}
}
}
/*
własna klasa filtrująca na podstawie interfesju FilenameFilter
metoda accept zwraca true, jeżeli nazwa pliku pasuje do filtru
*/
class Filtr implements FilenameFilter {
private String nazwa;
public Filtr(String nazwa) {
this.nazwa = nazwa;
}
public boolean accept(File dir, String name) {
return (name.startsWith(nazwa));
}
}
Aby filtrować pliki tworzę klasę Filtr implementując interfejs FilenameFilter. Metoda accept() stwierdzi czy plik pasuje do wzorca. Używam prostego warunku porównania Stringów: name.startsWith(nazwa). Jeżeli plik zaczyna się od podanej nazwy, metoda zwróci true, a więc pliki i katalogi pasujące do nazwy będę mógł bez problemu wyłapać.
Pliki i katalogi pasujące do wzorca pobieram metodą list() z przekazanym obiektem filtrującym. Sprawdzam, czy są jakieś pliki na liscie, jeżeli nie, wychodzę z programu. Jeżeli są, sprawdzam czy pasujący plik nie jest przypadkiem katalogiem (tych nie usuwam). Jeżeli nie, kasuję go metodą delete() i wypisuję komunikat o usunięciu.
Jak podpiąć polecenia wykonywane po kliknięciu guzików JButton?
PROBLEM
Chcesz wykonać dowolny kod w momencie kliknięcia na guziki JButton.
ROZWIĄZANIE
To prosty układ guzików w panelu JPanel. Dwa guziki, które po kliknięciu będą zmieniać etykietę znajdującą się poniżej, jak na rysunku:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class okno extends JFrame implements ActionListener {
JLabel etykieta;
JButton g1, g2;
public okno() {
setTitle("Okienko");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(200, 100);
g1 = new JButton("Akcja 1");
g1.addActionListener(this);
g2 = new JButton("Akcja 2");
g2.addActionListener(this);
JPanel p = new JPanel(new GridLayout(3, 1));
p.add(g1);
p.add(g2);
p.add(etykieta = new JLabel("", JLabel.CENTER));
add(p);
setContentPane(p);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == g1) {
etykieta.setText("wybrane 1");
}
if (e.getSource() == g2) {
etykieta.setText("wybrane 2");
}
}
public static void main(String[] args) {
new okno();
}
}
W metodzie main() od razu wywołuję konstruktor okno(). Tutaj ustawiam parametry okienka, tworzę nowe guziki i etykietę, w której będę pokazywał komunikaty. Guziki mają przypiętą metodę addActionListener(), abym mógł nasłuchiwać zdarzenia.
W metodzie actionPerformed() sprawdzam, od którego guzika pochodzi zdarzenie kliknięcia i wypisuję na etykiecie stosowny komunikat. Oczywiście w tym miejscu możesz umieścić dowolne inne instrukcje do wykonania.
Jak wygenerować liczbę losową z przedziału od x do y włącznie?
PROBLEM
Chcesz wygenerować liczbę losową z podanego przedziału, np. dowolną liczbę całkowitą między 1 i 30.
Generowanie liczb losowych to podstawa wszystkich procesów losowych lub pseudolosowych, stosowanych w matematyce lub rozrywce (gry). Zobacz jak wygenerować losową wartość całkowitą z podanego zakresu.
import java.util.*;
import java.awt.*;
import java.applet.*;
public class Test extends Applet {
Random generator = new Random();
public int losowa(int min, int max) {
int n = max - min + 1;
int i = Math.abs(generator.nextInt()) % n;
return min + i;
}
public void paint(Graphics g) {
g.drawString("losowa liczba: "+losowa(1,8), 20, 30 );
}
}
Oprócz klas niezbędnych do stworzenia appletu, trzeba zaimportować klasę java.util.*, aby uzyskać dostęp do generatora liczb losowych. W programie na początku inicjujemy generator liczb losowych (Random generator), a następnie w metodzie paint(), która odpowiada za wyświetlenie napisu, wywołujemy funkcję losowa() zwracającą liczbę losową z zakresu od 1 do 8.
Do funkcji losowa() przekazujemy dwie liczby: minimalną i maksymalną liczbę zakresu, z którego zostanie wylosowana jedna liczba. Jeżeli chcesz uzyskać liczbę z zakresu od 1 do 10 (włącznie), wywołaj funkcję losowa(1,10). Funkcja zwróci jedną, wylosowaną z podanego zakresu liczbę całkowitą.
W funkcji tworzymy podzielnik modulo n, a następnie wywołujemy losową liczbę typu Integer za pomocą metody generator.nextInt(). Z uwagi na duży zakres (typu Integer) już na wstępie pozbywamy się liczb ujemnych za pomocą Math.abs() i dzielimy otrzymaną liczbę modulo n. Teraz wystarczy zwrócić otrzymaną liczbę powiększoną o wartość minimalną.
Inny sposób to skorzystanie z metody Math.random(), np.
int x = (int) (Math.random()*11);
Metoda Math.random() generuje liczbę losową zmiennoprzecinkową z zakresu od 0 (włącznie) do 1 (ale bez 1). Aby uzyskać liczbę całkowią (integer) należy posłużyć się rzutowaniem typów za pomocą polecenia (int). Gdy pomnożymy wynik przez x otrzymamy losową liczbę od 0 do x-1. A więc powyższy przykład losuje liczby od 0 do 10 włącznie.
jak stworzyć układ typowego formularza korzystając z układu SpringLayout?
PROBLEM
Chcesz ułożyć dane w dwóch rzędach, aby stworzyć typowy układ do formularzy - gdzie obok siebie są etykiety i pola do wypełnienia, jak na rysunku:
Java jest mało przyjazna jeżeli chodzi o typowe układy pól w interfejsach użytkownika. Większość układów wymaga sporego nagimnastykowania się. Tworząc typowe formularze, najczęściej korzystam z układu SpringLayout. Jest w miarę prosty do zastosowania i pozwala bardzo łatwo poukładać elementy względem innych elementów, a więc jest przyjazny dla różnych systemów operacyjnych czy rodzajów okienek/appletów i jednocześnie wygodny dla projektanta.
Na przykładzie mini-formularza pokażę Ci, jak posługiwać się tym formatem, przy okazji obsługując wpisane do pól dane i wyświetlając je poniżej formularza.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class test extends JApplet implements ActionListener {
JButton ok;
JLabel l1,l2,l3,wynik;
JTextField t1,t2;
public void init() {
String left = "West", right = "East", top = "North", bottom = "South";
SpringLayout layout = new SpringLayout();
JPanel p = new JPanel(layout);
p.add(l1 = new JLabel("imie:"));
p.add(t1 = new JTextField(10));
p.add(l2 = new JLabel("nazwisko:"));
p.add(t2 = new JTextField(10));
p.add(ok = new JButton("OK"));
ok.addActionListener(this);
p.add(wynik = new JLabel(""));
// t1 - x i y:
layout.putConstraint(left, t1, 100, left, p);
layout.putConstraint( top, t1, 5, top, p);
// t2 - x i y:
layout.putConstraint(left, t2, 0, left, t1);
layout.putConstraint( top, t2, 5, bottom, t1);
// ok - x i y:
layout.putConstraint(left, ok, 0, left, t2);
layout.putConstraint( top, ok, 5, bottom, t2);
// l1 - x i y:
layout.putConstraint(right, l1, -5, left, t1);
layout.putConstraint( top, l1, 0, top, t1);
// l2 - x i y:
layout.putConstraint(right, l2, -5, left, t2);
layout.putConstraint( top, l2, 0, top, t2);
// wynik - x i y:
layout.putConstraint(left, wynik, 5, left, p);
layout.putConstraint( top, wynik, 5, bottom, ok);
getContentPane().add(p);
}
public static void main(String[] args) {
JApplet applet = new test();
JFrame f = new JFrame();
f.setContentPane(applet);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300,150);
applet.init();
f.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == ok) {
wynik.setText("Dane: "+t1.getText()+" "+t2.getText());
}
}
}
Aplet nie jest skomplikowany. Samo utworzenie wyglądu SpringLayout i dodanie elementów formularza również jest typowe. Kluczem jest zrozumienie pozycjonowania elementów.
Nie lubię pozycjonowania nazywanego stronami świata. Wolę pojęcia przyziemne, a więc angielskie nazwy góra (top), dół (bottom), lewo (left), prawo (right). Dlatego wprowadziłem dodatkowe zmienne zastępujące kierunki. A teraz do dzieła
// t1 - x i y:
layout.putConstraint(left, t1, 100, left, p);
layout.putConstraint( top, t1, 5, top, p);
To współrzędne pierwszego pola tekstowego t1. Pierwsza linijka ustala położenie pola w poziomie (X), druga w pionie (Y). Parametrów metody putConstraint() jest dziwnie dużo, prawda? Ale bardzo pomagają pozycjonować elementy względem innych. Składnia pozycjonowania jest następująca: • krawędź zaczepienia elementu wstawianego • obiekt wstawiany • pozycja • krawędź obiektu względem którego będzie ustawiana pozycja elementu • obiekt względem którego będzie ustawiana pozycja
Czyli przekładając pierwszą linię na ludzki język: lewa krawędź pola t1 ma być odsunięta o 300 pikseli od lewej krawędzi panelu p. Druga linia do oś Y, a więc: górna krawędź pola t1 ma być odsunięta o 5 pikseli od górnej krawędzi panelu p.
Identycznie ustawiam kolejne elementy, np. pole t2:
// t2 - x i y:
layout.putConstraint(left, t2, 0, left, t1);
layout.putConstraint( top, t2, 5, bottom, t1);
W osi X: lewa krawędź pola t2 ma być odsunięta o 0 pikseli względem lewej krawędzi pola t1. W osi Y: górna krawędź pola t2 ma być odsunięta o 5 pikseli od dolenj krawędzi pola t1. Ustawiłem w ten sposób pole t2 względem pola t1. Wygodne, prawda?
A oto etykieta:
// l1 - x i y:
layout.putConstraint(right, l1, -5, left, t1);
layout.putConstraint( top, l1, 0, top, t1);
W osi X: prawa krawędź etykiety l1 ma być odsunięta o -5 pikseli od lewej krawędzi pola t1. W osi Y: górna krawędź etykiety l1 ma być odsunięta o 0 pikseli od górnej krawędzi pola t1. Powoduje to, że etykieta przylega do lewej krawędzi pola, ale zachowując odstęp 5 pikseli.
W identyczny sposób układam resztę elementów pozycjonując je względem innych elementów.
Jak pobrać z pola tekstowego słowo i wypisać go w innym polu od tyłu po kliknięciu w guzik?
PROBLEM
Chcesz stworzyć applet z dwoma polami tekstowymi, gdzie w jednym wpisujesz słowo, a po kliknięciu w guzik, w drugim pojawia się słowo wypisane od tyłu, jak na obrazku:
ROZWIĄZANIE
Podobnie skonstruowane skrypty bardzo często stosowane są w praktyce. Zwykle dane pobierane są z okienek tekstowych, gdzie może je wpisać użytkownik, a następnie również wyświetlane są w okienkach lub jako grafika.
Stórzmy applet, który potrafi komunikować się z polami tekstowymi. Wykorzystamy przy okazji rozlokowanie elementów typu GridLayout i BorderLayout.
public class test extends Applet implements ActionListener {
TextField slowo, odTylu;
Button guzik;
public void actionPerformed(ActionEvent e) {
if (e.getSource() == guzik) {
odTylu.setText(""+new StringBuffer(slowo.getText()).reverse());
}
}
public void init() {
Panel p = new Panel();
p.setLayout(new GridLayout(4,2));
p.add(new Label());
p.add(new Label("Wpisz słowo:"));
p.add(new Label());
p.add(new Label("słowo:", Label.RIGHT));
p.add(slowo = new TextField());
p.add(new Label());
p.add(new Label("od tyłu:", Label.RIGHT));
p.add(odTylu = new TextField());
odTylu.setEditable(false);
p.add(new Label());
p.add(new Label());
p.add(guzik = new Button("Odwróć słowo!"));
guzik.addActionListener(this);
p.add(new Label());
setLayout(new BorderLayout());
add("North", p);
}
public void paint(Graphics g) { }
}
Applet będzie korzystał ze zdarzeń przypisanych do guzika, stąd implementujemy ActionListener dla klasy głównej. Deklarujemy dwie zmienne typu TextField (pola tekstowe) i jedną typu Button (przycisk).
W metodzie init() Tworzymy nowy panel, który ma rozkład typu GridLayout, czyli dzieli applet na rzędy i kolumny. Definiuję cztery rzędy i trzy kolumny, po czym wypełniam każdy z nich albo etykietami (czasem pustymi), albo polami tekstowymi.
Pole tekstowe odTylu za pomocą metody setEditable() zostało wyłączone, aby nie można było w nim pisać. Posłuży nam tylko do wyświetlania odwróconego tekstu.
Do guzika podpinamy nasłuchiwanie zdarzeń addActionListener(), czyli gdy guzik zostanie wciśnięty nastąpi wywołanie metody actionPerformed(), gdzie będzie można zareagować na wciśnięcie.
Pozostało nam jeszcze stworzyć układ typu BorderLayout() (północ, południe, wschód, zachód, centrum) i dodać gotowy panel na północy ("North"), czyli na górze appletu.
Metoda reagująca na zdarzenie jest prosta - sprawdza czy pochodzi ono od guzika, a jeżeli tak, wstawia do pola odTylu tekst pobrany za pomocą metody getText() z pola slowo.
Metoda reverse() realizuje obrót łańcucha znaków, z tym, że jest ona przypisana do obiektu StringBuffer(), stąd należy go wcześniej utworzyć.
Jak sprawdzić czy w pliku tekstowym występuje szukany ciąg znaków stosując wyrażenia regularne?
PROBLEM
Chcesz pobrać zawartość pliku i wyszukać w nim dowolnych ciągów znaków, korzystając z wyrażeń regularnych.
ROZWIĄZANIE
To bardzo przydatna czynność, szczególnie wtedy gdy planujesz przeszukiwać wiele plików tekstowych. Wystarczy tekst odczytać i sprawdzić czy występuje w nim określony ciąg znaków. Dopasowane ciągi znaków możesz także łatwo wypisać na ekran, zobacz:
import java.io.*;
import java.util.regex.*;
public class test {
public static void main(String args[]) {
String linia, txt=null;
try {
BufferedReader b = new BufferedReader(new FileReader("test.java"));
while((linia = b.readLine()) != null) txt+=linia+"\n";
Pattern p = Pattern.compile("while.*");
Matcher m = p.matcher(txt);
while(m.find()) System.out.println("Jest: "+m.group());
// System.out.println(txt);
} catch (IOException e) {
System.out.println("Błąd: "+e);
}
}
}
Klasa java.io.* pozwala operować na plikach, a java.util.regex.* daje dostęp do metod operujących na wyrażeniach regularnych. Odczytujemy plik standardowo, linia po linii, umieszczając go w całości w zmiennej txt.
Następnie trzeba stworzyć obiekt Pattern, który reprezentuje skompilowany wzorzec wyrażenia regularnego. Obiekt można stworzyć korzystając z metody compile(), która skompiluje wzorzec. Nasze wyrażenie regularne to "while.*", czyli fragment kodu źródłowego z tego przykładu.
Teraz za pomocą metody Pattern.matcher() możemy przekazać tekst, w którym będzie dopasowywany wzorzec (jest w całości w zmiennej txt).
Następnie w pętli while() wystarczy odczytywać znalezione dopasowania do wzorca i wypisywać je na ekran. W ten sposób możesz stwierdzić, czy szukane wyrażenie występuje w pliku czy nie. Nasze wyrażenie dopasowało trzy ciągi znaków:
Jest: while((linia = b.readLine()) != null) txt+=linia+"\n";
Jest: while.*");
Jest: while(m.find()) System.out.println("Jest: "+m.group());
Jak wykonać rozwijane pole wyboru?
PROBLEM
Chcesz stworzyć rozwijane pole wyboru, jak na rysunku:
ROZWIĄZANIE
Rozwijane pola wyboru są wygodnymi elementami formularzy. To sposób aby szybko wybrać jedną z wielu wartości, np. województwo lub zdefiniowany kolor. W poniższym przykładzie wybiorę element listy, a po kliknięciu na guzik "OK" odczytam ustawiony indeks i wartość z listy.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class test extends JApplet implements ActionListener {
JButton ok;
JLabel wybor;
JComboBox lista;
public void init() {
JPanel p = new JPanel();
String[] dane = {"pozycja 0", "pozycja 1", "pozycja 2", "pozycja 3"};
lista = new JComboBox(dane);
p.add(lista);
p.add(ok = new JButton("OK"));
ok.addActionListener(this);
p.add(wybor = new JLabel(""));
getContentPane().add(p);
}
public static void main(String[] args) {
JApplet applet = new test();
JFrame f = new JFrame();
f.setContentPane(applet);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300,150);
applet.init();
f.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == ok) {
wybor.setText("indeks: "+lista.getSelectedIndex()+", "+lista.getSelectedItem());
}
}
Listę możesz utworzyć za pomocą obiektu JComboBox(), przekazując do niego tablicę typu String z wartościami listy. Aby odczytać indeks wybranej wartości użyj metody getSelectedIndex(), a samą wartość odczytasz metodą getSelectedItem().
Jak odczytać zawartość prostego pliku XML?
PROBLEM
Chcesz odczytać i wypisać zawartość prostego pliku XML.
ROZWIĄZANIE
Niech nasz plik zawiera informacje o osobach - imiona i nazwiska wystarczą:
<?xml version="1.0" encoding="iso-8859-2"?>
<ludzie>
<osoba>
<imie>Jan</imie>
<nazwisko>Kowalski</nazwisko>
</osoba>
<osoba>
<imie>Anna</imie>
<nazwisko>Nowak</nazwisko>
</osoba>
</ludzie>
Odczytam go i wypiszę na ekranie dane osób w następującej postaci:
imie: Jan
nazwisko: Kowalski
imie: Anna
nazwisko: Nowak
To prosty sposób na przetwarzanie nieskomplikowanych plików XML. Oto kod:
import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class test {
public static void main (String args[]) {
try {
DocumentBuilderFactory bdf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = bdf.newDocumentBuilder();
Document xml = db.parse(new File("dane.xml"));
NodeList osoby = xml.getElementsByTagName("osoba");
for(int i=0;i<osoby.getLength();i++ ) {
NodeList osoba = osoby.item(i).getChildNodes();
for(int j=0;j<osoba.getLength();j++ ) {
if (osoba.item(j).getNodeType()== Node.ELEMENT_NODE) {
if (osoba.item(j).getNodeName()=="imie") {
System.out.print("imie: "+osoba.item(j).getTextContent());
}
if (osoba.item(j).getNodeName()=="nazwisko") {
System.out.print("\nnazwisko: "+osoba.item(j).getTextContent());
}
}
}
System.out.print("\n\n");
}
} catch (Exception e) {
System.out.print(e);
}
}
}
Pod odczytaniu i sparsowaniu pliku dane.xml muszę w jakiś sposób wydobyć rekordy osób. Najłatwiej jest odczytać wszystkie gałęzie osoba i przenieść je do listy o tej samej nazwie w skrypcie. Będą to dwa elementy.
W pętli for pobieram dzieci tej gałęzi i sprawdzam czy są to elementy (Node.ELEMENT_NODE), a nie np. puste pola typu text. Następnie ustalam, czy nazwa elementu to imie lub nazwisko, jeżeli tak, wypisuję zawartość gałęzi tekstowej.