Programowanie w Javie wykład 5 Pobieranie danych (skaner, okna dialogowe) Operacje na napisach (klasy String, StringTokenizer, StringBuffer, StringBuilder ) Podstawy wyrażeń regularnych Treści prezentowane w wykładzie zostały oparte o: Barteczko, JAVA Programowanie praktyczne od podstaw, PWN, 2014 Barteczko, JAVA Uniwersalne techniki programowania, PWN, 2017 http://docs.oracle.com/javase/8/docs/ C. S. Horstmann, G. Cornell, Java. Podstawy, Helion, Gliwice 2013
Wprowadzanie danych: okna dialogowe 2 Do wprowadzania danych możemy użyć skanera (klasy Scanner z pakietu java.util) dialogów wejściowych (statycznych metod showinputdialog z klasy JOptionPane z pakietu javax.swing. Zapis: String s=joptionpane.showinputdialog("tekst:"); spowoduje otwarcie okna dialogowego z komunikatem "Tekst:", w którym będzie można wprowadzić tekst. Po kliknięciu przycisku OK wprowadzony łańcuch będzie dostępny za pomocą zmiennej s, natomiast jeśli zamkniemy dialog przyciskiem Cancel, to zmienna s będzie mieć wartość null.
Wprowadzanie danych: okna dialogowe Do pokazania wyników w okienku komunikatów możemy użyć statycznej metody showmessagedialog z klasy JOptionPane z pakietu javax.swing. Zapis: JOptionPane.showMessageDialog(null,"Komunikat"); pokaże okno z komunikatem "Komunikat"(null mówi m.in., że okno ma być wycentrowane) Przykład. import static javax.swing.joptionpane.*; public class Powitanie { public static void main(string[] args) { String imie = showinputdialog("podaj swoje imię"); if (imie==null) imie=""; showmessagedialog(null, "Witaj "+ imie); 3
4 Wprowadzanie danych: klasa Scanner Standardowe wejście jest reprezentowane przez publiczną stałą z klasy System: System.in. Wygodnym sposobem czytania ze standardowego wejścia jest użycie klasy Scanner z pakietu java.util: import java.util.*; public class ScanString{ public static void main(string[] args){ Scanner scan = new Scanner(System.in); //parametr konstruktora - źródło danych System.out.println("Podaj imię:"); String name = scan.next(); //metoda next() wczytuje napis //niezawierający białych znaków System.out.println("Witaj " + name);
Wprowadzanie danych: klasa Scanner Wprowadzanie liczb z konsoli za pomocą skanera metody przekształcające postać znakową liczb w ich kod binarny: nextint()- dla liczb całkowitych, nextdouble()- dla liczb rzeczywistych. Uwaga: używamy, jako separatora miejsc dziesiętnych, ze względu na domyślnie polskie ustawienia regionalne - tzw. locale. Aby je zmieniać: Scanner scan=new Scanner(System.in).useLocale(new Locale("en")); import java.util.*; public class ScanNumbers{ public static void main(string[] args){ Scanner scan = new Scanner(System.in); System.out.print("Podaj liczbę całkowitą: "); int i = scan.nextint(); System.out.print("A teraz rzeczywistą: "); double d = scan.nextdouble(); System.out.println("Wprowadzono " + i + " " + d); 5
6 Wprowadzanie danych: klasa Scanner Metody klasy Scanner: hasnextint(), hasnextdouble(), itd. pozwalają na sprawdzenie czy w strumieniu wejściowym kolejny element jest odpowiednio liczbą całkowitą, rzeczywistą, itd. Przykład. Pętla zaporowa wymuszająca podanie przez użytkownika liczby rzeczywistej: //import java.text.numberformat; import java.util.scanner; public class ScannerTest { public static void main(string[] args) { System.out.print("Podaj liczbę rzeczywistą: "); Scanner scan = new Scanner(System.in); while (!scan.hasnextdouble()) { scan.next();//czytamy ta "nie liczbe" System.out.print("Podaj jeszcze raz: "); double d = scan.nextdouble(); System.out.println("Podałeś: " + d); //Aby sformatować liczbę zgodnie z Locale: //NumberFormat.getInstance().format(d)
Wprowadzanie liczb - okna dialogowe Gdy w polu tekstowym podamy jakąś liczbę, to będzie ona dostępna jako napis String. Do przekształcania łańcuchów na liczby całkowite używamy metody: int liczba=integer.parseint(napis); Jeśli przekształcany napis nie reprezentuje liczby całkowitej, to zostanie wyrzucony wyjątek NumberFormatException. import static javax.swing.joptionpane.*; public class ParseIntTest { public static void main(string[] args) { String s1 = showinputdialog("podaj liczbę: "); if (s1!=null) { int i=integer.parseint(s1); showmessagedialog(null,"kwadrat = " + (i*i)); //if //main //class 7
Wprowadzanie liczb - okna dialogowe 8 Podobnie, stosując metodę double Double.parseDouble(String s); możemy przekształcić znakową reprezentację liczb rzeczywistych w ich postać binarną. Metoda ta nie uwzględnia lokalizacji i zawsze za separator miejsc dziesiętnych przyjmuje.. import java.lang.*; public class DoubleDemo { public static void main(string[] args) { String str = "50"; double retval = Double.parseDouble(str); System.out.println("Value = " + retval); //Value = 50.0
9 Wprowadzanie liczb - okna dialogowe + skaner Skaner możemy łączyć z oknami dialogowymi, może on bowiem pobierać dane m.in. z napisów: import java.util.*; import static javax.swing.joptionpane.*; public class ScanNumsFromString { public static void main(string[] args) { String s = showinputdialog("podaj 2 liczby "+ "rozdzielone spacjami"); if (s!=null) { Scanner scan = new Scanner(s); int suma=scan.nextint()+scan.nextint(); showmessagedialog(null,"suma = " + suma);
10 Klasa String W klasie String znajdziemy wiele użytecznych metod przeznaczonych do operowania na łańcuchach znakowych. Kolejne znaki napisów występują na pozycjach, które są indeksowane od 0. Części napisów (łańcuchów znakowych) określa się terminem "podłańcuch" (substring). Obiekty klasy String są niemodyfikowalne (ang. immutable) - to znaczy napisów klasy String nie możemy zmieniać (np. do napisu dodać inny). W wielokrotnie używanej przez nas konkatenacji łańcuchów wynikowy napis, powstający z dodania do łańcucha znakowego innego napisu, jest nowym obiektem i np. w takiej sekwencji: String s = "Ala"; s = s + " ma kota"; tworzony jest nowy obiekt, zawierający napis "Ala ma kota" i referencja do niego przypisywana jest zmiennej s, która poprzednio zawierała referencję do napisu "Ala". Zwróćmy też uwagę, że w klasie String nie ma żadnych metod pozwalających modyfikować istniejący obiekt-napis.
Klasa String -wybrane metody 11 Poniżej przedstawimy kilka wybranych metod. Większość metod (wszystkie metody niestatyczne) używana jest "na rzecz" obiektów klasy String; o obiekcie na rzecz którego wywołano metodę mówimy ten napis. String(char[] c) konstruktor, tworzy napis z tablicy znaków char data[] = {'a', 'b', 'c'; String str = new String(data); String(byte[] b) konstruktor, tworzy napis z tablicy bajtów (używana jest domyślna strona kodowa do przekształcania bajtów na znaki) char charat(int index) - zwraca znak na pozycji, oznaczonej indeksem index. Pierwsza pozycja ma indeks 0. int compareto(string anotherstring) - porównuje dwa napisy: ten (this) na rzecz którego użyto metody oraz przekazany jako argument. Metoda zwraca 0, gdy napisy są takie same. Jeżeli się różnią, to - gdy występują w nich różne znaki - zwracana jest wartość: this.charat(k) - anotherstring.charat(k), gdzie k - indeks pierwszej pozycji, na której występuje różnica znaków. Jeżeli długość napisów jest różna (a znaki napisów są takie same w części określanej przez długość krótszego napisu) - zwracana jest różnica długości: this.length() - anotherstring.length(). Oznacza to, że wynik jest ujemny, gdy ten (this) łańcuch poprzedza leksykograficznie (alfabetycznie) argument (anotherstring) oraz dodatni - gdy ten łańcuch jest leksykograficznie większy od argumentu.
12 Klasa String -wybrane metody int comparetoignorecase(string str) - porównuje leksykograficznie dwa napisy, bez rozróżnienia małych i wielkich liter. boolean endswith(string suffix) - zwraca true, gdy napis kończy się łańcuchem znakowym podanym jako argument, false - w przeciwnym razie. boolean equals(object anobject) - zwraca true, gdy anobject jest takim samym co do zawartości napisem jak ten napis; w każdym innym przypadku - zwraca false. boolean equalsignorecase(string anotherstring) - j.w. - ale bez rozróżniania małych i wielkich liter. byte[] getbytes() zwraca tablicę bajtów reprezentujących ten napis w domyślnej stronie kodowej String example = "This is an example"; byte[] bytes = example.getbytes(); try { byte[] utf8bytes = example.getbytes("utf8"); System.out.println("Text UTF8: " + utf8bytes);//[b@7852e922 catch (UnsupportedEncodingException ex) { ex.printstacktrace(); System.out.println("Text: " + bytes);//[b@4e25154f
Klasa String -wybrane metody int indexof(string str) - zwraca indeks pozycji pierwszego wystąpienia w danym napisie napisu podanego jako argument str; jeżeli str nie występuje w tym napisie - zwraca -1 String str = "Welcome home"; System.out.println(str.indexOf( 'o' )); //4 int indexof(string str, int fromindex)- poszukuje pierwszego wystąpienia napisu str poczynając od pozycji oznaczonej przez indeks fromindex; zwraca indeks pozycji na której zaczyna się str lub -1 gdy str nie występuje w tym napisie. System.out.println(str.indexOf( 'o',5)); //9 int lastindexof(string str) - jak indexof - ale zwracany jest indeks pozycji ostatniego wystąpienia System.out.println(str.lastIndexOf( 'e' ));//11 int length() - zwraca długość napisu String replace(char oldchar, char newchar) - zwraca nowy obiekt klasy String, w którym zastąpiono wszystkie wystąpienia znaku oldchar na znak newchar. System.out.println(str.replace('h', 'H')); //Welcome Home 13
Klasa String -wybrane metody boolean startswith(string prefix)- zwraca true, gdy napis zaczyna się podanym jako argument łańcuchem znakowym; false - w przeciwnym razie. String str = "Welcome home"; System.out.println(str.startsWith("Welc") );//true String substring(int beginindex) - zwraca podłańcuch tego łańcucha znakowego zaczynający się na pozycji o indeksie beginindex (do końca łańcucha) String s="hello"; System.out.println(s.substring(2));//llo String substring(int beginindex, int endindex) - zwraca podłańcuch tego łańcucha jako nowy obiekt klasy String. Podłańcuch zaczyna się na pozycji o indeksie beginindex, a kończy (uwaga!) - na pozycji o indeksie endindex-1. Długość podłańcucha równa jest endindex beginindex. String s2 = "abcde".substring(2,3); System.out.println(s2);//c char[] tochararray()- znaki łańcucha -> do tablicy znaków (typ char[]). 14
Klasa String -wybrane metody 15 String tolowercase() - zamiana liter na małe. System.out.println("HELLO".toLowerCase());//hello String touppercase() - zamiana liter na duże. System.out.println("Hello".toUpperCase());//HELLO String trim() - usuwa znaki spacji, tabulacji, końca wiersza itp. tzw. białe znaki z obu końców łańcucha znakowego. Zwraca wynik jako nowy łańcuch. System.out.println(" Ala ma kota ".trim());//ala ma kota static String valueof(char c) - zwraca wartość typu char jako napis. static String valueof(char[] data) - zwraca napis złożony ze znaków z tablicy. String s3= String.valueOf(new char[]{'a','l','a'); System.out.println(s3); //ala static String valueof(double d) - zwraca znakową reprezentację liczby typu double. static String valueof(int i) - zwraca znakową reprezentację liczby typu int.
Klasa String 16 Uwaga. Zanim napiszemy fragment wykonujący określone zadanie na napisach, sprawdźmy w dokumentacji czy w klasie String nie ma metod, które by nam to ułatwiły. Kiedy porównujemy jakieś napisy, to zwróćmy uwagę na Czy białe znaki (spacje, tabulacje) ewentualnie okalające napis są istotne w porównaniu; jeśli nie to użyjmy metody trim() Czy chcemy porównać napisy, uwzględniając wielkość liter; jeśli nie, to użyjmy metod equalsignorecase lub ujednolićmy wielkość liter w napisach metodami touppercase()lub tolowercase() Przykład. Użytkownik odpowiada w dialogu wejściowym na pytanie. Uwzględnijmy to, że może wprowadzić niepotrzebne spacje lub użyć różnej wielkości liter. String ans=joptionpane.showinputdialog ("Czy mam wykonać program?"); if (ans!= null){ ans=ans.trim().touppercase(); switch(ans){ case "TAK": case "T" : execute();
Klasa String nowe metody w JDK11 W JDK11 wprowadzono nowe metody w klasie String: strip() metoda usuwa wiodące i następujące po słowie znaki białe stripleading() usuwa białe znaki przed słowem. striptrailing() usuwa białe znaki na końcu napisu var napis =" jakis tekst "; System.out.println("*"+ napis+"*"); System.out.println("*"+ napis.stripleading()+"*"); System.out.println("*"+ napis.striptrailing()+"*"); System.out.println("*"+ napis.strip()+"*"); Output: * jakis tekst * *jakis tekst * * jakis tekst* *jakis tekst* 17
Klasa String nowe metody w JDK11 18 isblank() metoda zwraca true jeśli napis jest pusty lub zawiera tylko znaki białe; w przeciwnym razie zwraca false. var value =""; System.out.println(value.isBlank()); //true repeat(n) metoda zwraca n- krotną konkatenacje napisu var tekst ="tekst"; System.out.println(tekst.repeat(5)); //tekstteksttekstteksttekst lines() metoda zwraca strumień stringów podzielonych znakami końca linii. Linie w strumieniu są w takiej kolejności jak w stringu. String t ="pierwsza linia \n druga linia \n"; System.out.println(t.lines().collect(Collectors.toList())); //[pierwsza linia, druga linia ]
19 String metoda join W Javie 8 do klasy String dodano metody statyczne join(separator, zestaw_napisów) gdzie zestaw_napisów to albo argumenty-napisy oddzielone przecinkami, albo tablica napisów, albo kolekcja napisów. Metoda wstawia podany separator między elementy zestawu_napisów i zwraca tak utworzony String: Przykład: String res= String.join("-", "A", "B", "C"); System.out.println(res); //A-B-C int n=10; String[] stab = new String[n]; int sum=0; for (int i=1; i<=n; i++){ sum+=i; stab[i-1]=string.valueof(i); System.out.println(String.join("+",stab)+" = "+sum); //1+2+3+4+5+6+7+8+9+10 = 55
String - przykład 21 public class ZnajdzAnd{ public static void main(string[] args) { // Tekst do analizy String text = "To be or not to be, that is the question;" + " Whether 'tis nobler in the mind to suffer" + " the slings and arrows of outragous fortunę," + " or to take arms against a sea of troubles," + " and by opposing end them?"; int licznikand = 0;// liczy wystąpienia and String andstr = "and"; // podłańcuch do wyszukania int index = text.indexof(andstr); // pierwsze and while(index >= 0) { ++licznikand; index += andstr.length() ; //przeskocz do pozycji //po pierwszym znalezionym and index = text.index0f(andstr, index); System.out.println("Tekst zawiera " + licznikand + " and");
Rozbiór tekstów : metoda split Bardzo elastyczne możliwości analizy tekstów dają wyrażenia regularne. Do rozbioru tekstów zalecana jest metoda split z klasy String: public String[] split(string regex) Wywołanie txt.split(sep) zwraca tablicę symboli z tekstu txt rozdzielonych separatorami pasującymi do wzorca podanego przez napis- wyrażenie regularne sep. Przykład 1: String[] result = "this is a test".split("\\s"); for (int i=0; i<result.length; i++) System.out.println(result[i]); run: this is a test 22
Rozbiór tekstów : metoda split Przykład 2:(separator pojedyncza spacja, w tekście 2 spacje) String[] s = "Ala ma kota".split(" "); System.out.println(s.length); for (int i=0; i<s.length; i++) System.out.println("*"+s[i]+"*"); run: 4 *Ala* ** *ma* *kota* Przykład 3:(separator jedna lub więcej spacji, w tekście 2 spacje) String[] s = "Ala ma kota ".split(" +");//" +"jedna lub więcej spacji System.out.println(s.length); for (int i=0; i<s.length; i++) System.out.println("*"+s[i]+"*"); run: 3 *Ala* *ma* *kota* 23
Rozbiór tekstów : metoda split Przykład 4:(w tekście 2 spacje i spacja z przodu, sep: >=1 spacji) String[] s = " Ala ma kota ".split(" +");//1 lub więcej spacji System.out.println(s.length); for (int i=0; i<s.length; i++) System.out.println(s[i]); run: 4 Ala ma kota Tablica zawiera cztery elementy z których pierwszy jest pustym napisem, gdyż elementy tablicy zwracanej przez metodę split są podnapisami z których każdy jest zakończony podnapisem pasującym do wyrażenia regularnego lub końcem wiersza. Przykład 5 (to samo przy pomocy klasy StringTokenizer) String s = " Ala ma kota "; int i = 0; StringTokenizer st = new StringTokenizer(s); while (st.hasmoretokens()) { String el = st.nexttoken(); System.out.println(el); i++; System.out.println(i); run: Ala ma kota 3 24
Scanner 26 Klasa java.util.scanner pozwala na łatwy rozbiór informacji tekstowej zawierającej napisy i dane typów prostych. Możliwości: działa na klasie String, plikach (File), strumieniach, kanałach, np. Scanner sc = new Scanner(System.in); Scanner sc1 = new Scanner(new File("myNumbers")); String input = "1 fish 2 fish red fish blue fish"; Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*"); do parsowania używa wyrażeń regularnych (w tym prostych separatorów, ale również dowolnych złożonych wyrażeń), łatwo rozbija teksty na wiersze (String nextline(), boolean hasnextline()), umie wyróżnić i skonwertować dane typów prostych (a także BigDecimal), pozwala na rozbiór, polegający nie tylko na wyróżnianiu symboli rozdzielonych separatorami, ale również na wyróżnianiu symboli pasujących do podanego wyrażenia regularnego (metoda findintext(...), metoda skip(...)), sposób rozbioru można zmieniać w trakcie skanowania tekstu, m.in. stosując rozliczne metody next...(), w tym takie, które pozwalają podawać różne wyrażenia regularne. pozwala na zlokalizowany rozbiór danych.
Scanner 27 Wybrane metody: String next() - pobieranie kolejnych elementów (ang. token) (napisów rozdzielonych separatorem domyślnie białe znaki) boolean hasnext() - sprawdza czy jest dostępny kolejny element String nextline() - pobieranie kolejnych wierszy boolean hasnextline() - sprawdza czy jest kolejna linia int nextint() - pobieranie kolejnego elementu jako liczbę całkowitą boolean hasnextint() - sprawdzanie czy następny element jest liczbą całkowitą int nextdouble() - pobieranie kolejnego elementu jako liczbę rzeczywistą boolean hasnextdouble() - sprawdzanie czy następny element jest liczbą rzeczywistą Scanner usedelimiter(string regex) - ustawia separator skanera na separator skonstruowany na podstawie parametru
Skaner - przykład 28 Przyklad. Mamy dany napis zawierający w kolejnych liniach liczby całkowite oddzielone znakiem tabulatora. Policzyć sumę tych liczb. public class Skaner1{ public static void main(string[] args) { String s1 = "1\t2\t3\n4\t5\t6"; Scanner scan1 = new Scanner(s1); int suma = 0; while (scan1.hasnextline()) { Scanner scan2 = new Scanner(scan1.nextLine()).useDelimiter("\\t"); while (scan2.hasnextint()) suma += scan2.nextint(); System.out.println("Suma = " + suma);