1. Formatowanie liczb i dat
Klasa java.utill.Formatter zapewnia możliwości formatowania danych.
Tworząc formator (za pomocą wywołania konstruktora) możemy
określić:
- destynację formatowanych danych (dokąd mają być zapisane), którą m.in.
może być String, StringBuffer, plik tekstowy,
- lokalizację
(ustawienia regionalne, reprezentowane przez obiekt
klasyLocale), wpływającą m.in. na reprezentację liczb i dat,
- stronę kodową (do kodowania napisów) -
m.in. dla plików i Stringów
Uwaga: formatory dla plików powinny być po użyciu zamykane lub wymiatane
(close(), flush()),
co powoduje zamknięcie lub wymiecenie buforów tych destynacji.
Formatowanie polega na wywołaniu jednej z dwóch wersji metody format
(na rzecz fornatora):
Formatter
format(String format, Object... arg)
Formatter
format(Locale l, String format, Object... arg)
Druga z tych metod pozwala na podanie lokalizacji, m.in. wpływającej na sposób formatowania liczb.
Łańcuch formatu (zmienna format) zawiera dowolne ciągi znaków oraz specjalne elementy
formatujące.
Dalej następują dane do "wstawienia"
w łańcuch formatu
w miejsce elementów formatu i do sformatowania podług zasad określanych przez te elementy
(zmienna
liczba argumentów dowolnego typu - formalnie Object). Dzięki
autoboxingowi
nie ma problemu z formatowaniem danych typów prostych.
Dla uproszczenia dostępne są:
- statyczne metody format w
klasie String, pozwalające na uzyskiwanie sformatowanych napisów,
- metody format i printf
(działające tak samo) w klasach PrintStream i PrintWriter,
wyprowadzające sformatowane napisy "na wyjście" (np. na standardowe
wyjście lub do pliku).
Elementy formatu mają następującą ogólną postać:
%[arg_ind$][flagi][szerokość_pola][.precyzja]konwersja
gdzie:
- arg_ind$
- numer argumentu (z listy argumentów arg) do sformatowania przez dany
element; numeracja zaczyna się od 1; poczynając od drugiego
elmentu formatu można w tym miejscu zastosować znak < , co oznacza,
że dany element ma być zastosowany wobec argumentu uzytego w poprzednim
formatowaniu,
- flagi - znaki modyfikujące sposób formatowania (są różne dla różnych typów konwersji),
- szerokość_pola - minimalna liczba znaków dla danego argumentu w wynikowym napisie,
- .precyzja
- liczba pokazywanych miejsc dziesiętnych (dotyczy liczb rzeczywistych)
lub maksymalna liczba wyprowadzanych znaków (dotyczy np. napisów),
- konwersja
- określa jak ma być traktowany i formatowany odpowiadający danemu
elementowi argument - np. jako liczba rzeczywista albo jako napis albo
jako data.
Uwaga: nawiasy kwadratowe oznaczają opcjonalność.
Symboli konwersji jest b. dużo, dla różnych symboli mogą być stosowane też dodatkowe flagi.
Wszystko to jest opisane w sposób systematyczny w dokumentacji (proszę sięgać).
Tutaj przedstawione zostaną wybrane konwersje i flagi.
Wybrane konwersje - skrót
Konwersja | Może być stosowana wobec | Wynik |
s lub S | dowolnych danych | Jeżeli argument jest null - napis "null": w przeciwym razie jeżeli klasa arg na to zezwala - wynik wywołania arg.formatTo(...) w przeciwnym razie wynik wywołania arg.toString() Uwaga: użycie jako symbolu konwersji dużego S spowoduje zamianę liter napisu na duże. |
c lub C | typów reprezentujących znaki Unicode | znak Unicode |
d | typów reprezentujących liczby całkowite | liczba całkowita (dziesiętna) |
f | float, double, Float, Double, BigDecimal | liczba rzeczywista z separatorem miejsc dzisiętnych |
tH | danych reprezentujących czas, czyli: long, Long, Calendar, Date | godzina na zegarze 24-godzinnym-2 cyfry (00-23) |
tM | minuty - 2 cyfry (00 - 59) |
tS | sekundy - 2 cyfry (00-60) |
tY | rok - 4 cyfry (np. 2008) |
tm | miesiąc - 2 cyfry (01-12) |
td | dzień miesiąca - 2 cyfry (01 -31) |
tR | czas na zegarze 24 godzinnym sformatowany jako "%tH:%tM" |
tT | czas na zegarze 24 godzinnym sformatowany jako "%tH:%tM:%tS" |
tF | data sformatowana jako "%tY-%tm-%td" |
Wśród flag na szczególną uwagę zasługują:
'-' - wynik wyrównany w polu do lewej (domyslnie jest wyrównany do prawej),
'+' - wynik zawiera zawsze znak (dla typów liczbowych),
' ' - wynik zawiera wiodąca spację dla argumentów nieujmenych (tylko dla typów liczbowych).
Zatem,
aby uzyskać sformatowane wyniki w poprzednim przykładowym programie
(liczbę z dwoma miejscami dziesiętnymi, datę w postaci
rok-miesiąc-dzień) możemy napisać:
import java.util.*;
public class Format1 {
public static void main(String[] args) {
double cena = 1.52;
double ilosc = 3;
double koszt = cena * ilosc;
System.out.printf("Koszt wynosi %.2f zł", koszt);
System.out.printf("\nData: %tF", Calendar.getInstance());
}
}
Wynik:
Koszt wynosi 4,56 zł
Data: 2008-08-14
Warto
tu zwrócić uwagę na to, że dla lokalizacji polskiej liczba pokazywana
jest z przecinkiem jako separatorem miejsc dziesiętnych.
Aby uzyskac kropkę można napisać:
System.out.printf(Locale.ROOT, "Koszt wynosi %.2f zł", koszt);
W tym przypadku stała statyczna Locale.ROOT oznacza neutralną lokalizację (bez wybranego kraju i języka).
Kilka innych przykładów pokazuje program na wydruku.
import javax.swing.*;
public class Format2 {
public static void main(String[] args) {
System.out.println("Wyrównany wydruk tablicy (po 2 elementy w wierszu)");
int[] arr = { 1, 100, 200, 4000 };
int k = 1;
for (int i : arr) {
System.out.printf("%5d", i);
if (k%2 ==0) System.out.println();
k++;
}
// Zastosowanie znaku < (element formatu stosowany wobec argumentu z poprzedniego formatowania)
System.out.println("Zaokraglenia");
System.out.printf("%.3f %<.2f %<.1f", 1.256 );
// Znak < szczególnie przydatny w datach/czasie
Calendar c = Calendar.getInstance();
c.set(Calendar.MONTH, 1);
System.out.printf("\nW roku %tY i miesiącu %<tm mamy %d dni", c, c.getActualMaximum(Calendar.DATE) );
// Oczywiście możemy formatować do Stringów
String dateNow = String.format("%td-%<tm-%<tY", System.currentTimeMillis());
JOptionPane.showMessageDialog(null, dateNow);
}
}
Wynik działania programu (konsolę i okno dialogowe) pokazuje rysunek.
2. Interfejs Formattable
Dla
symbolu konwersji s lub S jeżeli klasa drugiego argumentu metody format
na to pozwala, to do formatowania zostanie użyta metoda formatTo.
Jest
to możliwe tylko wtedy gdy klasa argumentu "do sformatowania"
implementuje interfejs Formattable i definiuje jego jedyną metodę:
public void formatTo(Formatter formatter, int flags, int width, int precision)
Jeśli teraz za pomocą jakiegoś formatora następuje formatowanie obiektu z użyciem symbolu konwersji s lub S, to:
- wywoływana jest metoda formatTo z klasy obiektu,
- metodzie formatTo jest przekazywany formator (jako parametr formatter), flagi formatowania (parametr flags), szerokość pola (parametr width) i precyzja (parametr precision),
- w
metodzie formatTo możemy sprawdzić wartości przekazanych informacji
(np. locale formatora, flagi, szerokość precyzje) i na tej podstawie
podjąć odpowiednie działania przetwarzające obiekt do
wynikowego napisu,
- przed zwróceniem sterowania z metody formatTo wywołujemy metodę format przekazanego formatora,
- sformatowany napis będzie przez formator zapisany do odpowiedniej destynacji (np. wyprowadzony na standardowe wyjście).
Sprawdzić
jakich użyto flag formatowania możemy za pomocą porównywania ze stałymi
statycznymi klasy java.util.FormattableFlags. Dostępne są stałe o
następujących nazwach:
- ALTERNATE - oznacza, że należy użyć alternatywnej formy sformatowania obiektu (w formacie użyto znaku #),
- LEFT_JUSTIFY - sformatowany napis będzie wyrównany w polu do lewej (w formacie użyto znaku -),
- UPPERCASE - zamiana liter na duże (użyto wielkiej litery w symbolu konwersji np. S, zamiast s).
Przykładowy
program pokazuje zastosowanie metody formatTo do łatwej zmiany sposobu
wyprowadzania informacji o obiektach klasy Person. W trybie
normalnym wyprowadzane jest nazwisko, w trybie alternatywnym
(zastosowana flaga #) - nazwisko i imię. W metodzie formatTo, na
podstawie przekazanych informacji wybieramy formę formatowania,i
budujemy napis formatu na podstawie przekazanej informacji, po czym za
jego pomocą formatujemmy obiekt, używając przekazanego formatora.
import java.util.*;
import static java.util.FormattableFlags.*;
public class Person implements Formattable{
private String fname;
private String lname;
public Person(String fname, String lname) {
this.fname = fname;
this.lname = lname;
}
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
String txt = lname;
if ((flags & ALTERNATE) == ALTERNATE) txt += ' ' + fname;
String fs = "%";
if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) fs += '-';
if (width >= 0) fs += width;
if (precision >= 0) fs += "."+precision;
fs += ((flags & UPPERCASE) == UPPERCASE) ? "S" : "s";
formatter.format(fs, txt);
}
public static void main(String[] args) {
Person e = new Person("Jan", "Kowalski");
System.out.printf("%#s\n", e);
System.out.printf("%20s\n", e);
System.out.printf("%#30S\n", e);
System.out.printf("%#.10S\n", e);
}
}
Wynik działania programu:
Kowalski Jan
Kowalski
KOWALSKI JAN
KOWALSKI J