Archive | December, 2011

Ubuntu (pl)

31 Dec

(click here for English version of this post: Ubuntu (en))

Naszło mnie, kiedy znajomy informatyk (od razu przepraszam za uproszczenie) znalazł na półce w Tesco napój o nazwie Ubuntu Cola (poniżej zdjęcie ze strony http://www.ethicalsuperstore.com/) i umieścił zdjęcie na Facebooku w ramach branżowego żartu. Moi drodzy, Ubuntu Cola (żeby nie było, nie jestem w stanie zignorować komizmu tej nazwy) to nie podkradanie marki dystrybucji Linuksa, tylko nawiązanie do tej samej idei, której zawdzięcza ona swoją nazwę.

Ubuntu to afrykańska filozofia, której zwolennikiem jest między innymi Nelson Mandela. Samo słowo Ubuntu bywa tłumaczone jestem, ponieważ jesteśmy. U podstaw tej humanistycznej filozofii leży odrzucenie indywidualizmu, poszanowanie wartości ludzkiego życia, wspólnota, resocjalizacja w miejsce kary, dialog i tolerancja. Desmond Tutu (swoją drogą, laurerat Pokojowej Nagrody Nobla) pisze (tłumaczenie własne, oryginał w angielskiej wersji postu, jeśli ktoś zna oficjalne i ładniejsze, zachęcam do wskazania), a ja cytuję za The African Philosophy Reader:

Ubuntu to sama istota bycia człowiekiem, to część daru, który Afryka ofiaruje światu. Ubuntu to gościnność, dbanie o innych, wychodzenie im naprzeciw. Wierzymy, że człowiek jest człowiekiem poprzez innych, że moje człowieczeństwo jest nierozerwalnie związane z Twoim. Jeśli odmawiam Ci człowieczeństwa, niepowstrzymanie odmawiam go także sobie. Pojedyncza istota ludzka to oksymoron. Z tego powodu pragniemy pracować dla dobra ogółu, gdyż człowieczeństwo wyraża się we wspólnocie i przynależności.

Pasuje do ruchu wolnego oprogramowania, prawda? :)

Słowo to w kontekście innym niż linuksowy poznałam podczas organizowanych przez British Council warsztatów Międzykulturowi Nawigatorzy, które nauczyły mnie dość sporo, ale o tym zaraz. Pamiętam dość długo blok zajęć, bogato ilustrowany materiałem filmowym, w którym przedstawiciele afrykańskich plemion mówili o tym, jak wdrażają tę ideę w życie. Jeśli ktoś nie ma picia lub jedzenia, należy o niego zadbać. Jeśli ktoś nie ma siły do pracy, należy mu pomóc i go wyręczyć, i tak dalej. Przez cały czas moja wschodnio-zachodnia głowa próbowała znaleźć odpowiedź na jedno pytanie: „jak ochronić się przed oszustwem”? Jak długo można „nie mieć sił do pracy”? A co, jeśli ta osoba przez całe życie będzie odprężała się kosztem wysiłków całego plemienia, i nigdy nie zwróci nawet ułamka otrzymanych przysług i wysiłków? Odpowiedź była jedna: „nic nie szkodzi”, nie chodzi o to, żeby je zwrócił, nie to jest celem.

Skoro już jesteśmy przy tym temacie (spójrz jeszcze raz na zdjęcie z puszką Ubuntu Coli), w Poznaniu otworzył się właśnie pierwszy sklep w całości poświęcony produktom Fair Trade, choć poszczególne produkty od dłuższego już czasu można dostać w niektórych poznańskich sklepach, takich jak Alma czy sklepik Folwarku Wąsowo. Pisząc ten post sprawdziłam wreszcie lokalizację sklepu FT – okazało się, że mijam go często w drodze do pracy (tzn. wtedy, gdy jadę samochodem i parkuję pod domem mojej siostry). Wkrótce zdam relację.

Wracając do Nawigatorów… Nie pamiętam nawet, jak to się stało, że zgłosiłam się do tego programu. Przez kilka miesięcy jeden weekend w miesiącu miałam zajęty spotkaniami z grupą najbardziej zdeterminowanych i świadomych własnego celu ludzi, jakich spotkałam w życiu. Wydaje mi się, że na spotkaniach każdy z nas w ogromnym stopniu odnalazł siebie i nauczył się wiele o innych ludziach. Pod koniec warsztatów mieliśmy szansę aplikować o finansowanie projektów związanych z tematem integracji między kulturami, dzięki czemu kilka miesięcy później przeprowadziłam (wspólnie z kilkoma poznańskimi przyjaciółmi, pozdrowienia dla Ewy oraz Męża, który nie może się doczekać pojawienia się na blogu) warsztaty Babcie do komputerów! – cudowne doświadczenie, tyle że efektem ubocznym było stworzenie grupy spamerów w podeszłym wieku – może wykopię gdzieś różowe szablony maili, które do dziś co jakiś czas otrzymuję. Na razie zdjęcie, więcej na stronie pamiątkowej projektu.

Może ktoś ma ochotę na powtórkę?

Szczęśliwego Nowego Roku!

Wyrażenia regularne w Javie – figle i psikusy

14 Dec

(click here for English version of this post: Java Regex Gotchas)

Tradycyjnie zacznę od zestawu ostrzeżeń:

  1. Jeśli zaglądasz tu, szukając anegdotek i niezgorzkniałego narzekania: UCIEKAJ ile sił! Ten post należy do kategorii Java, nie chcesz tego zgłębiać. Obiecuję, że normalny wpis pojawi się wkrótce.
  2. Jeśli programujesz: poruszam tu bardzo podstawowe kwestie, które potrafią jednak dać się we znaki. Rozwiązanie opisanych tu niewinnych problemików kosztowało mnie trochę czasu i nerwów. Liczę, że kiedy jakaś zbłąkana dusza znajdzie się w tej samej sytuacji, Google zaprowadzi ją prosto w moje troskliwe ramiona.
  3. Jeśli jesteś moim liderem zespołu: tak, kolejny raz wracamy do historii o pociągach i spadochronach. Wolałbyś tego nie wiedzieć.

Jeśli nadal ze mną jesteś – do rzeczy. Spędziłam ostatnio trochę czasu z wyrażeniami regularnymi w Javie, ponieważ zajmowałam się czyszczeniem i normalizacją rekordów metadanych. Uważam, że zaszywanie tego typu przekształceń na sztywno w kodzie jest nadużyciem, dlatego przygotowałam narzędzie wczytujące z pliku zestaw reguł określających dla danego elementu metadanych warunek oraz akcję, która ma zostać wykonana po jego spełnieniu. Jedną z konsekwencji tego rozwiązania jest to, że wyrażenia regularne (określające warunki w regułach) muszą zostać wczytane z zewnętrznego pliku.

Oto miejsca, w których zachwiana została moja równowaga.

Psikus 1: znaki ucieczki w wyrażeniach wczytywanych z zewnętrznego pliku

Wyrażenia regularne dosyć często (regularnie ;) ) pojawiają się w kodzie, dlatego nie spodziewałam się żadnych problemów. Do czasu.

Nie pamiętam już oryginalnego przykładu, ale ten, który podam, jest wystarczająco podobny. Załóżmy, że wyrażenie (które chcemy wczytać z zewnętrznego pliku) w zwykłym kodzie Javy wygląda tak:

Pattern yearPattern = Pattern.compile("^\\d{2}[-/]\\d{2}\\s*w\\.$");

Linia przedstawia zakres wieków, do którego dopasuje się na przykład:

14-16 w.

Proste, prawda?

Prawda – aż do chwili, kiedy wyrażenie zostało wczytane z zewnętrznego pliku.

Nic nie działało. Łańcuchy znaków, które bez najmniejszych wątpliwości powinny były dopasować się do wyrażenia, przeszły niezauważone. Po godzinie analiz niebezpiecznie zbliżałam się do stanu, w którym myślałam, że oszalał albo świat dokoła mnie, albo ja, kiedy szczęśliwie nadeszła pora lunchu (12.00 – koledzy w pracy jedzą obiad w porze właściwej dla mojej babci Halinki). Opowiedziałam o problemie nad talerzem naleśników ze szpinakiem (jedno z trzech wegetariańskich dań w restauracji Omega). Jeden z nich kolegów zadał oczywiste w sumie pytanie – czy na pewno dobrze wyeskejpowałam (przyjaciele humaniści: przepraszam!!!) wszystkie znaki specjalne. I wtedy wreszcie nadeszło olśnienie: nie, nie zrobiłam tego dobrze. Przeeskejpowałam je.

Znaki specjalne, takie jak d, w wyrażeniu regularnym oznaczające cyfrę, należy poprzedzić backslashem (ukośnikiem wstecznym). W kodzie Javy konieczne jest wprowadzenie dodatkowego backslasha, gdyż musimy jeszcze odebrać specjalne znaczenie samemu backslashowi (musimy poprzedzić znak ucieczki znakiem ucieczki, brzmi to naprawdę dobrze). Tyle razy widziałam te dwa ukośniki w parze, że zupełnie zapomniałam o tym, że w zewnętrznym pliku należy użyć tylko jednego!

Nadal jestem winna Michałowi paczkę cukierków za tę sugestię.

Psikus 2: flagi

To bardzo proste. Załóżmy, że wyrażenie nie ma brać pod uwagę wielkości liter. Normalnie oznaczamy to tak:

Pattern yearPattern = Pattern.compile("^\\d{2}[-/]\\d{2}\\s*w\\.$",Pattern.CASE_INSENSITIVE);

Świetnie, tylko jak przekazać tę flagę, jeśli wyrażenie jest wczytywane z zewnątrz? Okazuje się, że flagę, poprzedzona znakiem zapytania, należy umieścić w nawiasie na początku wyrażenia. Ignorowanie wielkości liter (przy okazji, poznałam ostatnio nowe słowo – kasztowość) to literka i, zatem dodajemy (?i). Ostatecznie, w kodzie wyrażenie wygląda tak:

(?i)^\\d{2}[-/]\\d{2}\\s*w\\.$

a poza kodem tak:

(?i)^\d{2}[-/]\d{2}\s*w\.$

Psikus 3: String.replaceAll

Na deser…

W pewnym specjalnym przypadku reguła zamieniająca dopasowany fragment na podany łańcuch znaków została użyta w kontekście, w którym wymieniony miał zostać cały łańcuch. Na późniejszym etapie wprowadziłam osobną regułę obsługującą ten przypadek, jednak nie o tym mowa. W uproszczenie, wywołanie wyglądało tak:

String s = "string contents".replaceAll(".*","nowa zawartość")

Po wykonaniu się tego kodu spodziewałam się, że wartością s będzie:

nowa zawartość

skoro * jest zachłannym kwantyfikatorem, zatem .* powinno dopasować się do całego napisu niezależnie od okoliczności.

Wyobraźcie sobie moje zaskoczenie (czy raczej przerażenie), gdy okazało się, że s przyjęło wartość:

nowa zawartośćnowa zawartość

Próbowałam jeszcze użyć bardziej zaborczego kwantyfikatora *+, ale efekt był ten sam. Byłabym mniej zaskoczona, gdyby .* zostało dopasowane do każdej litery w łańcuchu. Jakim cudem dopasowało się dokładnie dwa razy?

Dalsze śledztwo wykazało, że (w Javie 6) przebieg akcji jest następujący:

  1. Cały łańcuch dopasowuje się do .* i jest zamieniany na łańcuch "nowa zawartość".
  2. Po dopasowaniu z oryginalnego łańcucha znaków nie zostaje nic, a raczej zostaje łańcuch "". Metoda replaceAll jeszcze raz sprawdza możliwość dopasowania i okazuje się, że "" także pasuje do .*, zatem pusty łańcuch również zostaje wymieniony.
  3. Zasadniczo można by kontynuować i w nieskończoność dodawać na końcu "nowa zawartość", jednak na szczęście (?) dana pozycja w łańcuchu znaków jest traktowana jako sprawdzona i wykonanie metody kończy się.

Mam nadzieję, że moje wyznania złagodzą cierpienia innej osoby w tej samej sytuacji.

Java Regex Gotchas

11 Dec

(po polsku: Wyrażenia regularne w Javie – figle i psikusy)

It is almost a tradition here. I am going to start with a list of disclaimers:

  1. If you come here for the storytelling and whining: RUN! This is the Java category, you do not want to read it. A proper post is coming soon.
  2. If you are a programmer: this is basic stuff. This is trivial. But it did cost me some time and some nerves, so I am writing this down to help other poor helpless creatures.
  3. If you are my team leader: yes, I know, this is the parachutes and trains story one more time. You wish I had not told you this.

If you are still with me, let us get to the point. I have recently spent some time working with regular expressions in Java because of a metadata cleaning and normalization task. I hate hardcoding this kind of stuff, so I proposed this set of rules in which you can define conditions on the contents of a certain metadata tag and then execute an action if there is a match. This means some regular expressions are read into the program from external rules files.

Here is where I stumbled.

Gotcha 1: escaping characters when the regex is read from an external source

I deal with regular expressions all the time, so I did not expect any problems. Then came this.

I do not remember the real example, here is one similar enough. Let us assume that the regex (that we want to create externally) in normal Java code looks like this:

Pattern yearPattern = Pattern.compile("^\\d{2}[-/]\\d{2}\\s*w\\.$");

It is a line representing a centuries range (w stands for wiek, which is Polish for a century), like this one:

14-16 w.

Straightforward, isn’t it? Well, yes, up to the point when I read the regex from an external rules file.

It did not work. It did not match any strings it was supposed to match. I had just started thinking that maybe I had gone crazy, but then, luckily, came the lunch time. I told the story to my colleagues over a plate of spinach pancakes (one of the 3 vegetarian dishes the restaurant/canteen offers), and one of them asked the obvious question: did you escape special characters properly? And then it struck me: no, I didn’t. I overescaped them.

Normally you introduce special characters, like d which stands for a digit, with a backslash. In Java code, however, you have to use two backlashes to escape the backlash which has a special meaning in Java code (you have to escape the escape character, how cool is that). But when you read the regex from an external file you do not do it! I had seen so many regular expressions in Java that I got used to those double backslashes. Fail.

I still owe Michał a box of candy for this suggestion.

Gotcha 2: flags

This one is easy. Say I want the regex to be case-insensitive. The normal way to do it is this:

Pattern yearPattern = Pattern.compile("^\\d{2}[-/]\\d{2}\\s*w\\.$",Pattern.CASE_INSENSITIVE);

Cool, but if I am reading the regex from a file, how to pass the case insensitive flag? Well, you add the flag in parenthesis at the beginning of the regex. Case insensivity is i, so you add (?i). Finally it looks like this:

(?i)^\\d{2}[-/]\\d{2}\\s*w\\.$

or, externally:

(?i)^\d{2}[-/]\d{2}\s*w\.$

Gotcha 3: String.replaceAll

And finally…

I had this special case when the replace regex rule was supposed to replace the whole contents of a matched string. I later wrote a dedicated rule for that, but that is not the point. Simplified, the call looked like this:

String s = "string contents".replaceAll(".*","new contents")

After the call I expected s to be

new contents

as * is a greedy quantifier, so .* should be matched to the whole contents no matter what.

Imagine my surprise (the horror) when s turned out to be

new contentsnew contents

I even tried the possesive quantifier *+, but the result was the same. I would be less surprised if .* was matched to every letter of the string, but why was it matched exactly twice?

Further investigation proved that what happens (Java 6) is this:

  1. The whole string is matched against .*, so it is replaced with "new contents".
  2. What is left after matching the string is "", so the method checks again and concludes that "" matches .*, so it also gets replaced with "new contents".
  3. Obviously you could continue with this logic and add "new contents” in an infinite loop at the end of the string, but fortunately (?) this position in the string is treated as checked, so the method ends.

I hope that this will help somebody in their suffering :)

Follow

Get every new post delivered to your Inbox.