FormatCzęść NR.1Postanowiłem odświeżyć sobie wszystkie metody z klasy String.
Jedna metoda String.Format daje tyle możliwości używania , że postanowiłem zrobić o niej oddzielny wpis.
Podstawowe Formatowanie
Do czego głównie służy String.Format. No cóż…
Główną cechą metody String.Format jest przerabianie listy argumentów na napis po czym umieszczanie jej w określonej kolejności.
Jak widzisz argumentem może być każdy obiekt, w końcu każdy z nich ma metodę wirtualną ToString() więc może być zmieniony na napis.
Oto przykład kodu z dwoma typami int i double.
int liInt = 64200;
double liDou = 6.42;
string[] s_re_2 = new string[10];
s_re_2[0] = string.Format("Wystąpił {0} błąd", liInt);
s_re_2[1] = string.Format(
"Cyfra int {0} ,a to cyfra double {1}",
liInt, liDou);
Obie liczby są zmieniane na string i umieszczane w określonej kolejności. W nawiasach klamrowych deklarujemy numer argumentu i inne parametry o czym później.
Dla napisu “Wystąpił {0} błąd” pierwszy argument zostanie umieszczony właśnie w miejscu {0}.
W Console.Writeline() można stosować podobne formatowanie.
Na pewno jest lepsze rozwiązanie niż to poniżej, zwłaszcza że sprawa się komplikuje wraz z większą liczbą argumentów, które nie są napisami.
s_re_2[1] = "Cyfra int " + liInt.ToString()
+ ",a to cyfra double " + liDou.ToString();
Warto też pamiętać o tym , że metoda String.Format nie jest odporna na błędy programistów. W wypadku podania złej liczby argumentów otrzymamy wyjątek FormatException.
Ten wyjątek może też pojawiać się, gdy formatowanie nie może zajść dla danego argumentu. O czym przekonasz się później w tym wpisie.
Nawet operując na stałych danych kompilator nie umie przechwycić złego indeksu i tak dalej.
Formatowanie String
Dużo opcji nie ma, jeśli chodzi o formatowanie napisów. Do dyspozycji mamy tylko możliwość dodania odpowiedniej ilości białych znaków przed albo po napisie. Liczbę białych znaków deklarujemy po numerze argumentu.
Kod | Rezultat |
string.Format("-->{0,40}<--", "Słowo"); | "--> Słowo<--" |
string.Format("-->{0,-40}<--", "Słowo"); | "-->Słowo <--" |
string.Format("-->{0,-40}<--", 12.34); | "-->12,34 <--" |
Ta sama sztuczka może być zastosowana do jakiekolwiek typu. Jak np. do typu double 12.34. Jednak jeśli chodzi o formatowanie napisów tutaj kończą się możliwości.
Formatowanie Liczb
Wartości liczbowe mogą być przedstawione w różny sposób. W sposób walutowy , naukowy i tak dalej. Używając string.format można nadać cyfrom określony napis.
Warto zaznaczyć , że w formatowaniu dużą rolę odgrywa ustawiona kultura. W moim programie format waluty skonwertował się na walutę polską, czyli “zł”. O tym, jak można przestawić domyślną kulturę w String.Format trochę później.
Specyfikator | Typ | Format | Wynik dla INT 64200 | Wynik dla double 6.42 |
c | Waluta | {0:c} | 64 200,00 zł | 6,42 zł |
d | Dziesiętny | {0:d} | 64200 | FormatExcepiton: Format specifier was invalid |
e | Naukowy | {0:e} | 6,420000e+004 | 6,420000e+000 |
f | Fixed point | {0:f} | 64200,00 | 6,42 |
g | Ogólny | {0:g} | 64200 | 6,42 |
n | Liczba z przecinkami do tysięcy | {0:n} | 64 200,00 | 6,42 |
r | Zaokrąglanie | {0:r} | FormatExcepiton: Format specifier was invalid | 6,42 |
x | Szesnastkowy | {0:x} | fac8 | FormatExcepiton: Format specifier was invalid |
Jak widzisz nie wszystkie operacje formatowania są dostępne dla liczb całkowitych, podobnie nie wszystkie operacje są dostępne dla ułamków. Przykładowo w zapisie szesnastkowym trudno zapisać ułamek liczbowy.
string[] s_re_2 = new string[8];
int liInt = 64200;
double liDou = 6.42;
s_re_2[0] = string.Format("Int: {0:c} , Dou: {1:c}",
liInt,liDou);
s_re_2[1] = string.Format(
"Int: {0:d} ,Dou: \"Format specifier was invalid.\""
, liInt);
s_re_2[2] = string.Format("Int: {0:e} , Dou: {1:e}", liInt,
liDou);
s_re_2[3] = string.Format("Int: {0:f} , Dou: {1:f}", liInt,
liDou);
s_re_2[4] = string.Format("Int: {0:g} , Dou: {1:g}", liInt,
liDou);
s_re_2[5] = string.Format("Int: {0:n} , Dou: {1:n}", liInt,
liDou);
s_re_2[6] = string.Format("Int: \"Format specifier was invalid.\" ,
Dou: {0:g}", liDou);
s_re_2[7] = string.Format("Int: {0:x4} , Dou: \"Format specifier was invalid.\"",
liInt);
Formatowanie cyfr na tym się nie kończy, otóż możemy jeszcze ustawiać określoną liczbę zer, jak i gdzie liczby mają być ustawione.
Specyfikator | Co to robi: | Przykład: | Dla float 120.011 | Uwagi |
0 | Ustawia zera | {0:0.00000} | 120,01100 | |
# | Ustawia cyfry | {0:[#].(#)(##)} | [120],(0)(11) | |
. | Punkt dziesiętny | {0:[#].(#)(##)} | 120,0 | |
, | Tysięczny | {0:0,0} | 120 | Musi być pomiędzy dwoma zerami. |
,. | Skalowanie liczby | {0:0,.0} | 0,1 | Dzieli liczbę przez 1000 i ją zaokrągla musiałem dodać zero by zobaczyć wynik inny niż 0. |
% | Procent | (0:0%} | 12001% | Mnoży przez 100 i dodaje symbol % |
e | Wykładnik zastępczy | {0:0e+0} | 1e+2 | Istnieje dużo pod formatów. |
; | Separator grupowy | {0:0;Zero} |
Kod, którym sprawdziłem działanie tych specyfikatorów w formatowaniu.
float liFlo = 120.011f;
string[] s_re_3 = new string[7];
s_re_3[0] = string.Format("{0:0.00000}", liFlo);
s_re_3[1] = string.Format("{0:[#].(#)(##)}", liFlo);
s_re_3[2] = string.Format("{0:0.0}", liFlo);
s_re_3[3] = string.Format("{0:0,0}", liFlo);
s_re_3[4] = string.Format("{0:,.}", liFlo);
s_re_3[5] = string.Format("{0:0%}", liFlo);
s_re_3[6] = string.Format("{0:0e+0}", liFlo);
Separator grupowy wymaga większego objaśnienia niż się wydaje.
Oto kod objaśniający działanie separatora.
double t1 = -123.4;
double t2 = 123.4;
double t3 = 0;
string[] s_re_7 = new string[3];
s_re_7[0] = string.Format("{0:#,##0.0;(#,##)Minus;Zero}", t1);
s_re_7[1] = string.Format("{0:#,##0.0;(#,##)Minus;Zero}", t2);
s_re_7[2] = string.Format("{0:#,##0.0;(#,##)Minus;Zero}", t3);
…oraz jego rezultat.
Jak widzisz ten specjalny separator pokazuje trzy różne style formatowania napisu w zależności od wartości liczbowej.
Dla wartości dodatniej wykona się format “#,##0.0” dla wartości ujemnej “(#,##)Minus” ,a dla zera “Zero”.
W niektórych sytuacjach może to być przydatne np. gdy chcemy ukryć wartość ujemną albo dać specjalne wyrażenie dla wartości zero.
Formatowanie liczb to przydatne operacje. Nigdy nie wiesz do czego mogą one się przydać.
int numerTel = 8005112;
string nt = string.Format("{0:(###)-####-BUT}", numerTel);
Są jeszcze jednak inne typy danych, które mogą być formatowane na różne sposoby.
Daty
To jeszcze nie koniec możemy także formatować w różny sposób daty czyli klasę System.DateTime.
Specyfikator | Typ | Przykład z System.DateTime.Now |
d | Short Date. Krótka data | 2011-07-04 |
D | Long Date. Długa data | 4 lipca 2011 |
t | Short Time. Krótki czas | 17:59 |
T | Long Time. Długi czas | 17:59:09 |
f | Full date & time. Pełna data i czas | 4 lipca 2011 17:59 |
F | Full date & time (long). Pełna data i czas (dłuższa) | 4 lipca 2011 17:59:09 |
g | Default date & time. Domyślna data i czas. | 2011-07-04 17:59 |
G | Default date & time (long). Domyślna data i czas. | 2011-07-04 17:59:09 |
M | Month day pattern. Miesiąc dzień wzór | 4 lipca |
r | RFC1123 date string. | Mon, 04 Jul 2011 17:59:09 GMT |
s | Sortable date string. Sortujący data. | 2011-07-04T17:59:09 |
u | Universal sortable, local time. Uniwersalna | 2011-07-04 17:59:09Z |
U | Universal sortable, GMT | 4 lipca 2011 15:59:09 |
Y | Year month pattern. Rok miesiąc wzór. | lipiec 2011 |
Mam wrażenie , że napis z u i U nie może być poprawnie sortowany.
Oto kod, którym sprawdziłem działanie wszystkich tych formatów.
string[] s_re_4 = new string[14];
DateTime d = System.DateTime.Now;
s_re_4[0] = string.Format("{0:d}", d);
s_re_4[1] = string.Format("{0:D}", d);
s_re_4[2] = string.Format("{0:t}", d);
s_re_4[3] = string.Format("{0:T}", d);
s_re_4[4] = string.Format("{0:f}", d);
s_re_4[5] = string.Format("{0:F}", d);
s_re_4[6] = string.Format("{0:g}", d);
s_re_4[7] = string.Format("{0:G}", d);
s_re_4[8] = string.Format("{0:M}", d);
s_re_4[9] = string.Format("{0:r}", d);
s_re_4[10] = string.Format("{0:s}", d);
s_re_4[11] = string.Format("{0:u}", d);
s_re_4[12] = string.Format("{0:U}", d);
s_re_4[13] = string.Format("{0:Y}", d);
W tabelce podałem wynik działania tego kodu ale nie zaszkodzi zrobić to jeszcze raz.
Do dyspozycji są też niestandardowe formaty dat.
Specyfikator | Typ | Przykład: | Wynik: |
dd | Dzień | {0:dd} | 04 |
ddd | Nazwa dnia | {0:ddd} | Pn |
ddddd | Pełen nazwa dnia | {0:dddd} | poniedziałek |
f,ff,fff… | Second fractions | {0:ff} {0:fff} | 47 i 471 |
gg,.. | Era | {0:gg} | A.D |
hh | Dwucyfrowa godzinna | {0:hh} | 07 |
HH | Dwucyfrowa godzinna format 24 | {0:HH} | 19 |
mm | Minuty | {0:mm} | 40 |
MM | Miesiące 01-12 | {0:MM} | 07 |
MMM | Skrócona nazwa miesiąca | {0:MMM} | lip |
MMMM | Pełna nazwa miesiąc | {0:MMMM} | lipiec |
ss | Sekundy | {0:ss} | 45 |
tt | AM albo PM | {0:tt} | “” |
yy | Rok dwucyfrowy | {0:yy} | 11 |
yyyy | Rok | {0:yyyy} | 2011 |
zz | Strefa czasowa offset, 2 cyfry | {0:zz} | +02 |
zzz | Strefa czasowa offset z minutami | {0:zzz} | +2:00 |
: | Separator | {0:ss:mm:hh} | 45:40:07 |
/ | Separator | {0:yyyy/dd/MM} | 2011-04-07 |
Oto kod, którym sprawdzałem działanie tych specyfikatorów może ci się przyda.
d = System.DateTime.Now;
string[] s_re_5 = new string[21];
s_re_5[0] = string.Format("{0:dd}", d);
s_re_5[1] = string.Format("{0:ddd}", d);
s_re_5[2] = string.Format("{0:dddd}", d);
s_re_5[3] = string.Format("{0:f}", d);
s_re_5[4] = string.Format("{0:ff}", d);
s_re_5[5] = string.Format("{0:fff}", d);
s_re_5[6] = string.Format("{0:gg}", d);
s_re_5[7] = string.Format("{0:hh}", d);
s_re_5[8] = string.Format("{0:HH}", d);
s_re_5[9] = string.Format("{0:mm}", d);
s_re_5[10] = string.Format("{0:MM}", d);
s_re_5[11] = string.Format("{0:MMM}", d);
s_re_5[12] = string.Format("{0:MMMM}", d);
s_re_5[13] = string.Format("{0:ss}", d);
s_re_5[14] = string.Format("{0:tt}", d);
s_re_5[15] = string.Format("{0:yy}", d);
s_re_5[16] = string.Format("{0:yyyy}", d);
s_re_5[17] = string.Format("{0:zz}", d);
s_re_5[18] = string.Format("{0:zzz}", d);
s_re_5[19] = string.Format("{0:ss:mm:hh}", d);
s_re_5[20] = string.Format("{0:yyyy/dd/MM}", d);
To byłoby na tyle, jeśli chodzi o daty.
Typ wyliczeniowy
Typ wyliczeniowy przechowuje wartość liczbową, która reprezentuje liczbę danej wartości oraz samą wartość.
Typ wyliczeniowy, który sprawdzę i sformatuję wygląda tak.
enum KsztaltChmury : byte
{
Pierzasta,
Warstwowa = 11,
Klebiaste = 41
}
Jak się domyślasz czas na kolejną tabelkę.
Specyfikator | Typ | Przykład: | Wynik: |
g | Domyślna flaga(jeśli jest dostępna to liczba) | {0:g} | Warstwowa |
f | Flaga, czyli wartość | {0:f} | Warstwowa |
d | Liczba | {0:d} | 11 |
x | Liczba w formacie hex. | {0:x} | 0B |
Kod testujący:
string[] s_re_6 = new string[4];
KsztaltChmury kcc = KsztaltChmury.Warstwowa;
s_re_6[0] = string.Format("{0:g}", kcc);
s_re_6[1] = string.Format("{0:f}", kcc);
s_re_6[2] = string.Format("{0:d}", kcc);
s_re_6[3] = string.Format("{0:x}", kcc);
A co z innymi wersjami kulturowymi.
Różnice kulturowe
No cóż, widząc jak obszerny jest ten temat postanowiłem przydzielić na niego oddzielny wpis.