Tematem budowania łańcuchów znaków zajmowałem się już kiedyś, badając efektywność tej operacji w C++ dla trzech różnych sposobów. Tym razem przyjrzę się sprawie z perspektywy bardziej niezależnej od języka. Ostatnio poczyniłem bowiem pewną obserwację. Zauważyłem mianowicie, że “tradycyjne” podejście do składania stringów – przez konkatenację kolejnych podciągów – staje się… hmm… niemodne, jeśli można tu użyć takiego określenia.
Ci, którzy kodują nieco dłużej niż ja pewnie zwrócą mi zaraz uwagę, że takie naprawdę tradycyjne podejście do problemu to to, które implementują funkcje z języka C podobne do printf
:
Polega ono na uprzednim zdefiniowaniu wzorca tekstu (tzw. formatu, tutaj jest to drugi argument fprintf
), zawierającego symbole zastępcze takie jak %s
czy %d
. Są one następnie zastępowane podanymi wartościami (tutaj: tag
oraz errno
) w celu wyprodukowania finalnego ciągu. Nie ma tu jawnej operacji łączenia dwóch napisów. Zamiast tego mały parser przegląda podany wzorzec i w miejscu symboli zastępczych wstawia do wyniku podane wartości, przepisując bez zmian resztę znaków.
Wkrótce jednak nadszedł C++ z klasami string
i ostream
, oferującymi podobne możliwości bez użycia wzorców. Zamiast tego posługiwały się operatorami +
i <<
w celu wyprodukowania tekstowego wyjścia:
cerr << "[" << tag << "] Error (errno=" << errno << ")" << endl;[/cpp]
To działa, ale - jak można zobaczyć w porównaniu - wygląda często znacznie mniej przejrzyście. Nie dziwi więc, że w tym względzie historia zdaje się zatoczyć koło.
Obecnie bowiem operacja formatowania napisów (w sensie powyższej funkcji fprintf
, a nie określania czcionki czy stylu tekstu) jest wbudowana w wiele nowoczesnych, popularnych języków programowania, jak choćby C#, Pythona czy Javę. Najczęściej też, co ciekawe, trzyma się ona z grubsza oryginalnej składni wzorców pochodzącej jeszcze z C, dodając oczywiście jakieś własne, bardziej zaawansowane rozszerzenia:
Czasami jednak – jak widać wyżej – tak nie jest :) Na obecność mechanizmu formatowania łańcuchów znaków możemy aczkolwiek liczyć w każdym szanującym się języku. I to łącznie z C++, gdzie wszystko prawie wszystko, czego w nim pierwotnie nie ma, zawiera się w jakiejś bibliotece boost – w tym przypadku jest to boost::format.
Python 3.x (później chyba backport do Pythona 2.6) wprowadza metodę format dla obiektu String. Działa to i wygląda podobnie jak to dla C#. Ogólnie bardzo rozbudowane, ze sporymi możliwościami. Najlepiej przeczytać dokumentację: Format String Syntax.
To formatowanie bardzo często ułatwia życie. Zamiast wielu “<<" piszemy sobie po prostu wzorzec i podstawiamy argumenty :)
Jest jeszcze “Gracz {PLAYER} zdobyl {ITEM} na mapie {MAP}”. Duzo fajniej sie sprawdza przy np. tlumaczeniach niz kombinowanie z rozszerzeniami printf do zmiany kolejnosci argumentow.
@Dab: zmiana kolejności parametrów jest możliwa w kilku różnych API (np. QT -> QString::format przyjmuje argumenty w postaci %1, %2 itd.). Podobnie C#.
Formatowanie i/lub zastępowanie przez regexp są zdecydowanie lepsze w przypadku tłumaczenia na inne języki – gdzie często kolejność słów/wyrażeń może być inna niż w języku bazowym (zwykle angielskim).
@Asm: Dodatkowo formaty można sobie trzymać w zasobach, a potem rozwijać je przy odczytywaniu.
Jakie języki wspierają takie formatowanie po kluczach, btw? Na pewno Python i wydaje mi się, że C# też. Nie wiem, jak inne.