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óż…

StringFormat

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.

format

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.

Wyjątek Format

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.

KodRezultat
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.

SpecyfikatorTypFormatWynik dla INT 64200Wynik dla double 6.42
cWaluta{0:c}64 200,00 zł6,42 zł
dDziesiętny{0:d}64200FormatExcepiton:
Format specifier was invalid
eNaukowy{0:e}6,420000e+0046,420000e+000
fFixed point{0:f}64200,006,42
gOgólny{0:g}642006,42
nLiczba z przecinkami do tysięcy{0:n}64 200,006,42
rZaokrąglanie{0:r}FormatExcepiton:
Format specifier was invalid
6,42
xSzesnastkowy{0:x}fac8FormatExcepiton:
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.

SpecyfikatorCo to robi:Przykład:Dla float 120.011Uwagi
0Ustawia zera{0:0.00000}120,01100 
#Ustawia cyfry{0:[#].(#)(##)}[120],(0)(11) 
.Punkt dziesiętny{0:[#].(#)(##)}120,0 
,Tysięczny{0:0,0}120Musi być pomiędzy dwoma zerami.
,.Skalowanie liczby{0:0,.0}0,1Dzieli 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 %
eWykładnik zastępczy{0:0e+0}1e+2Istnieje 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.

Seperator

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);


Telefon

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.

SpecyfikatorTypPrzykład z System.DateTime.Now
dShort Date. Krótka data2011-07-04
DLong Date. Długa data4 lipca 2011
tShort Time. Krótki czas17:59
TLong Time. Długi czas17:59:09
fFull date & time. Pełna data i czas4 lipca 2011 17:59
FFull date & time (long). Pełna data i czas (dłuższa)4 lipca 2011 17:59:09
gDefault date & time. Domyślna data i czas.2011-07-04 17:59
GDefault date & time (long). Domyślna data i czas.2011-07-04 17:59:09
MMonth day pattern. Miesiąc dzień wzór4 lipca
rRFC1123 date string.Mon, 04 Jul 2011 17:59:09 GMT
sSortable date string. Sortujący data.2011-07-04T17:59:09
uUniversal sortable, local time. Uniwersalna2011-07-04 17:59:09Z
UUniversal sortable, GMT4 lipca 2011 15:59:09
YYear 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.

datatimenow

Do dyspozycji są też niestandardowe formaty dat.

SpecyfikatorTypPrzykład:Wynik:
ddDzień{0:dd}04
dddNazwa dnia{0:ddd}Pn
dddddPeł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
hhDwucyfrowa godzinna{0:hh}07
HHDwucyfrowa godzinna format 24{0:HH}19
mmMinuty{0:mm}40
MMMiesiące 01-12{0:MM}07
MMMSkrócona nazwa miesiąca{0:MMM}lip
MMMMPełna nazwa miesiąc{0:MMMM}lipiec
ssSekundy{0:ss}45
ttAM albo PM{0:tt}“”
yyRok dwucyfrowy{0:yy}11
yyyyRok{0:yyyy}2011
zzStrefa czasowa offset, 2 cyfry{0:zz}+02
zzzStrefa 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ę.

SpecyfikatorTypPrzykład:Wynik:
gDomyślna flaga(jeśli jest dostępna to liczba){0:g}Warstwowa
fFlaga, czyli wartość{0:f}Warstwowa
dLiczba{0:d}11
xLiczba 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.