Parsowanie spisu imienin #2

Swego czasu ‘popełniłem’ jeden mały skrypt na użytek własny, który miał mi przerobić listę imienin ściągniętą z Wikipedii (strona: Imieniny) na zbiór zapytań SQL, które potrzebne mi były do jednego projektu. Patrząc z perspektywy czasu napisałem mały koszmarek.

Mimo to, w końcu to nieświadomie, miałem kilka wejść ludzi którzy poszukiwali następujących fraz:

  • skrypt imienin
  • sql z imieninami
  • linux imieniny
  • imiona.txt
  • szablon daty imienin
  • imieniny daty
  • imieniny.sql
  • spis imienin
  • itp…

To właśnie zapotrzebowanie czytelników, choć być może i jednorazowych, skłoniło mnie, w ramach ćwiczeń z Pythonem, do ucywilizowania mojego dzieła. Z marnych 40 linii kodu powstało ich jakieś 200 (ściślej, wliczając komentarze i puste linie 250). Ale mimo to uważam go za lepszy. Przede wszystkim wiem więcej o wyrażeniach regularnych, a kod jest obiektowy, przez co łatwo jest – mam nadzieję – dostosować go do indywidualnych potrzeb użytkownika.

Przykłady uruchomienia

Zamieszczam przykładowe wyniki działania mojego najnowszego dziecka:

Ogólna zasada działania

Ponieważ nie wiem, jaką kto założył sobie bazę danych, jakie ma w niej tablice i relacje między nimi, nie napisałem klasy generującej SQL, ale zakładam, że na podstawie opisów oraz przykładów i przy odrobinie smykałki do Pythona łatwo będzie uzyskać to co tam się komu wymarzy.

Nie mniej jednak oto i krótki kurs: klasą która dostaje i formatuje dane na koniec, jest jakiś potomek klasy AbstractLineParser. Ważne jej metody, które należy przeciążyć to: _setNames oraz _setDates. Należy pamiętać, iż obie dostają jako parametry listy (bo jedno imię zwykle ma wiele dat, a imię też może występować w odmianach). Zawsze też dostaje się najpierw imiona, a potem daty do nich. Resztę można wywnioskować samemu, albo przebrnąć przez komentarze.

Kod skryptu można zobaczyć, lub pobrać z tego oto linka

Słowo końcowe

Cóż, moje testowanie błędów nie wykazało, jednak przestrzegam, przed imieniem: “Apollo(n)” (to chyba jedyny taki przypadek). Ignorowane też są imiona, z których czytelnik został odesłany – np. Adelgunda, zob. Adalgunda. Nie zabraniam modyfikacji, nawet zachęcam, nie biorę odpowiedzialności za wszelakie zło wyrządzone przez ten skrypt (no bez przesady, to tylko trochę kodu w Pythonie).

Na końcu chciałem podziękować (w kolejności chronologicznej): tym którzy komentowali mój poprzedni post, bo ich rady jakoś tam się przydały, następnie mojej Pani, która bez wątpienia zna się na Pythonie lepiej niż ja – bez złośliwości, wiem że to nie jest trudne – za pomoc.

Parsowanie spisu imienin

Potrzebowałem – potrzebuje:) bo wciąż jeszcze tego nie zrobiłem – tabele SQL z imieninami – imię i data jego imienin. Oczywiście potrzebowałem danych – to akurat było bardzo proste, odpowiedni spis znalazłem na Wikipedii

Mogłem szukać dalej i znaleźć coś łatwiejszego do parsowania, ale pomyślałem, że to przerobienie tego spisu będzie dość proste. I w sumie tak było.

Niewiele więc myśląc skopiowałem tekst strony i wkleiłem do pliku imiona.txt. Pierwszy problem – pozbyć linii – nie zawierających imion:

(...)
    * Aurelian — 4 lipca, 25 września
    * Aureliusz — 9 września, 13 września, 16 listopada


   (wróć do indeksu)

B [edytuj]

    * Balbin — 21 grudnia
    * Balbina — 31 marca, 2 grudnia
(...)

Wystarczyło wklepać w konsoli:

Tak, teraz trzeba było tylko sparsować plik tmp.txt - wymęczyłem w końcu skrypt:

{geshi lang=bash num=true}#!/usr/bin/env python #-*- coding: utf-8 -*- import re pat_name = re.compile(' *\* (?P.*) — ') month = { "stycznia":0, "lutego":1, "marca":2, "kwietnia":3, "maja":4, "czerwca":5, "lipca":6, "sierpnia":7, "września":8, "października":9, "listopada":10, "grudnia":11 } try: inFile = open("tmp.txt") for line in inFile: tmp = pat_name.search(line) if tmp: # Usuwanie \n line = line[:-1] # Usuwanie ostatniego znaku - o ile jest if line[-1:] == ',' or line[-1:] == '.': line = line[:-1] # Wydzielenie dat strdate = line.split(' — ')[1] # Wydzielamy imiona names = tmp.group('name').split(', ') if len(names) == 1: names = names[0].split(',') # Rozdzielamy daty dates = strdate.split(',') # Uzyskane wyniki - imiona: for name in names: print name # - daty ich imienin for date in dates: if date[0] == ' ': date = date[1:] print "data:", date, month[date.split(' ')[1]] finally: inFile.close()

Zadziało, zamieszczony skrypt jeszcze nie generuje skryptu SQL, ale to już tylko kwestia wklepywania danych do pliku miedzy odpowiednimi tekstami

Inne kwestie:

  • Po co to zamieszczam? Jako przykład – działa, a może się nawet komuś przydać. Z analizy cudzego kodu o którym wiemy, że jest poprawny, można się dowiedzieć paru rzeczy.
  • Spis z Wiki nie był idealny, tu brak spacji, tu przecinek za dużo itp – stąd jest w skrypcie klika dodatkowych ifów i nieco dziwne rozwiązania.
  • A.. miesiące mają przyporządkowane numery od 0 – to chyba akurat widać.