Perl vs. Visual Basic jeden příklad z praxe

Tento článek porovnává dvě různá řešení převodu dat z textového souboru do databáze v Microsoft Accessu z hledisk...


Tento článek porovnává dvě různá řešení převodu dat z textového souboru do
databáze v Microsoft Accessu z hlediska složitosti programu a doby potřebné k
provedení úkolu.

Největší slabinou naší mateřské rakouské firmy je její informační systém.
Říkáme mu žertem NOE, protože dobu jeho vzniku lze ve světle dnešního
raketového pokroku informačních technologií označit za období před potopou. NOE
ještě před rokem neumožňoval získávat některé důležité informační výstupy jinak
než ve formě tiskových sestav neboli reportů. Inu, počítačový středověk. Pro
vytváření statistik požadovaných českým vedením naší pobočky jsem si proto
vytvořil vlastní databázi v MS Accessu, kam jsem data z reportů načítal pomocí
poměrně složitých programových modulů ve Visual Basicu. Jedním z klíčových
zdrojů dat byl soubor účetních položek vnitropodnikového účetnictví. Ten jsem
si každý měsíc "natáhl" do tabulek, se kterými jsem potom prováděl různé
kejkle. Načtení měsíčních dat obvykle zabralo několik hodin, takže jsem je
pouštěl přes noc. Takhle to fungovalo k mé spokojenosti několik měsíců, dokud
nepřišel leden a s ním kolega Petr, který nutně potřeboval strukturu tržeb
jednoho svého klienta za celý rok. Svoje výsledky dostal asi za týden, protože
právě tak dlouho trvalo noční načítání dat za celý rok. Data jsem z databáze o
výsledné velikosti kolem 900 MB zase smazal, abych měl na svém PC nějaké místo.
Proto mě poněkud zaskočilo, když za mnou přišli další kolegové se stejným
požadavkem. Nakonec jsem problém vyřešil použitím Perlu k extrakci dat a jejich
uložení do vhodného formátu. Struktura vstupního textového souboru
Soubor obsahuje všechny účetní položky za určité období, uspořádané podle
jednotlivých účtů. Účet v tomto souboru je jednak účet klienta (kam se účtují
všechny fakturované položky za materiál, služby apod.), jednak protiúčty, tj.
sklad, účty nákladů a výnosů atd. Níže uvedený příklad je úmyslně zjednodušený,
protože jeho účelem není dokumentovat účetní metodiku firmy nebo prozrazovat
důvěrné informace. Každý účet má v sestavě své záhlaví, za kterým následuje
seznam položek za dané období (obvykle týden). Aby věc byla složitější, některá
data nemají v řádku pevné místo, ale následují za daty s proměnnou délkou.
Například označení účtu (číselné) má délku 3-9 znaků a bezprostředně za ním
následuje název zákazníka nebo příslušného vnitropodnikového účtu. Některé účty
zákazníků jsou ještě rozšířeny o jakési předčíslí, které se "přičítá" k číslu
účtu zákazníka, ale pro účely jakéhokoliv získávání informací je z
pochopitelných důvodů lepší toto předčíslí vést zvlášť. Jako perlička jsou
ještě do některých řádků vsunuty ESC kódy, ale naštěstí vždy dvouznakové. Tolik
stručná ukázka. V řádku položky znamená první číslo pořadové číslo položky v
systému, následuje částka, znaménko, datum splatnosti, stručný popis typu
položky a kontakt, resp. označení účtu zákazníka (akce). Ušetřím vás temných
detailů, jako je DPH apod. Postup řešení
Připomínám, že účelem zpracování tohoto souboru je dostat data do tabulky v
databázi. K tomu je potřeba:
Správně identifikovat první řádek (řádek účtu) Správně identifikovat řádek
účetní položky Extrahovat všechna data z prvního řádku Extrahovat všechna data
z řádku položky Získaná data zapsat ve vhodném formátu Identifikace
Ve Visual Basicu budeme potřebovat několik specializovaných podprogramů pro
rozlišení jednotlivých řetězců a také abychom dosáhli přehledné struktury
hlavního programu. Protože text kompletního modulu by zabral poměrně hodně
místa, uvedu pro ilustraci pouze nejsložitější případ datum. Několik poznámek k
dalším podprogramům 1. Identifikace data
Proti výše uvedené části řešení lze namítnout, že pro konverzi řetězců na
hodnoty typu datum je ve Visual Basicu určena funkce CDate. V dokumentaci k
této funkci je napsáno, že převede na typ Date libovolný řetězec, který
představuje platný zápis data. Bohužel, specifické vyjádření data použité v
našem textovém souboru je nám ve Visual Basicu k ničemu. 2. Identifikace účtu
Podprogram musí správně identifikovat označení účtu na "prvním" řádku (1-3
trojmístné skupiny číslic oddělené mezerou), případně předčíslí, a výsledek
uložit do vhodných proměnných. Po identifikaci a extrakci identifikátoru účtu
se musí rozpoznaný řetězec smazat (např. příkazem DataLine = Mid (DataLine,
Pos)), má-li být možné pracovat s bezprostředně následujícími textovými údaji.
3. Konverze částky
Výstup obsahuje desetinnou čárku a oddělovač tisíců (tečku). Teoreticky by bylo
možné tento formát ošetřit vhodnou funkcí nebo přímým přiřazením (např. v
Excelu to občas jde), ale v praxi je tento způsob těžko použitelný, protože
závisí na nastavení prostředí. Pro zajištění přenositelnosti je lepší speciální
podprogram. Zápis položky databázové tabulky
Ve Visual Basicu je vhodné napsat si na zápis položky databázové tabulky
podprogram s použitím globálních proměnných (vynecháváme textové údaje, které
by se v každé položce zbytečně opakovaly a které je ve skutečnosti možné
extrahovat zvlášť, případně lze předpokládat, že je máme v naší databázi již z
dřívějška). Fajnšmekři zrychlí chod vhodným použitím BeginTrans a CommitTrans.
Pokud by vaším cílem bylo napsat ve Visual Basicu konvertor místo "plniče
tabulek", podprogram SaveRecord by mohl vypadat třeba takhle: Šok: Řešení v
Perlu
Zatímco výsledný kód ve Visual Basicu měl několik desítek řádků a kvůli
přehlednosti byl rozdělen na několik podprogramů, celý program v Perlu
(convert.pl) je menší než 25 řádků (včetně komentářů). Použité programové
konstrukce jsou víceméně elementární, takže není potřeba je umisťovat do
podprogramů. Program se spouští příkazem: perl convert.pl vstupni_soubor >
vystupni_soubor

Komentář k textu programu
Účelem tohoto článku není podrobně se zabývat rysy jazyka Perl, které byly v
programu použity. Proto prosím chápejte následující poznámky jako pouhé narážky
vedoucí k pochopení funkce programu. Pokud vás zajímají podrobnosti, musím vás
odkázat na literaturu (např. beznadějně vyprodaná a v knihovnách dlouho dopředu
rezervovaná kniha Programování v jazyce Perl, man perl, on-line dokumentace
apod.).
Řádek 1 má význam v unixových shellech, kde díky němu není nutné explicitně
volat interpret Perlu. Řádek 2 definuje potřebné proměnné (s prázdným obsahem).
Není to nutné, ale pomáhá to udržet pořádek.
Řádky 3-5 definují asociativní pole (hash), v němž je každé zkratce měsíce
přiřazeno jeho pořadové číslo. Díky tomu se převod zkratky měsíce na pořadové
číslo zredukuje na použití triviálního výrazu (srov. podprogram InitMonths a
funkci MonthToNumber ve Visual Basicu).
Řádkem 6 začíná cyklus, v jehož každém průchodu se načte jeden řádek ze
vstupního souboru. Zápis v závorce odpovídá standardnímu vstupu, ale Perl v
duchu sobě vlastním automaticky přesměruje na standardní vstup obsah souboru,
jehož jméno je zadáno jako parametr v příkazové řádce. Je-li přečten celý
soubor, výraz , který je v podmínce cyklu while vlastně zkratkou pro
$_=, vrací hodnotu false a cyklus skončí.
Na řádku 7 se ze vstupního textu vymažou všechny ESC kódy. Regulární výraz v
podmínce na řádku 9 je děsný. Jeho smysl ovšem není v čitelnosti (leda pro
fanatiky regulárních výrazů), nýbrž v tom, že umožňuje popsat poměrně složitou
strukturu hledaného řádku v jediném výrazu. Porovnejte to se speciálními
podprogramy ve Visual Basicu a uvědomte si, kolik programových řádků je ve
Visual Basicu potřeba ke korektní detekci a zpracování hledaného řetězce.
Na řádcích 10 a 11 se nalezené fragmenty textu rovnou ukládají do proměnných a
z čísla účtu se vymažou nepotřebné mezery.
Na řádku 14 se nachází regulární výraz, jemuž má vyhovovat řádek obsahující
účetní položku. V těle podmíněného příkazu se využívá toho, že některé
fragmenty textu mají v tomto řádku pevnou pozici (použití funkce substr).
Na řádcích 17-20 se "čistí" a transformuje obsah proměnných $castka a
$splatnost, aby byl stravitelný pro importovací filtr Microsoft Accessu.
Řádek 21 efektivně využívá funkce join (oddělovač, seznam) k vytvoření řádky
dat oddělených tabelátorem. Tento formát je jedním ze standardních formátů, pro
něž má MS Access importní filtr v dynamické knihovně. Porovnání výkonnosti
Konverzí v Perlu byl vytvořen standardní formát textového souboru, jehož
načtení do Accessu je mnohonásobně rychlejší než původně použité importní
podprogramy ve Visual Basicu. Rychlost konverze lze s původním programem jen
stěží srovnat 800 MB dat v několika stovkách samostatných souborů se podařilo
zkonvertovat na stolním PC s Pentiem 200 asi za 20 minut. Samotný import takto
zkonvertovaného textu do tabulky v Accessu zabral přibližně 50-55 minut. V
původním řešení trval kompletní import souborů mnoho hodin (spíše několik dní).
Závěrem
Na uvedeném příkladu jsem chtěl ukázat, že efektivní a vyzrálé nástroje
pocházející z otevřeného prostředí unixových systémů a Linuxu mohou zefektivnit
činnost administrátorů i v prostředí Windows, kde jednoduché, účinné a levné
nástroje dlouho chyběly.

'Globalni promenne
Dim CapturedDate As Date
Dim Months(12) As String
'Tato funkce naplni pole Months(), ktere
'potrebujeme pro prevod data
Sub InitMonths
Months(1) = "JAN"
Months(2) = "FEB"
Months(3) = "MAR"
Months(4) = "APR"
Months(5) = "MAI"
Months(6) = "JUN"
Months(7) = "JUL"
Months(8) = "AUG"
Months(9) = "SEP"
Months(10) = "OKT"
Months(11) = "NOV"
Months(12) = "DEZ"
End Sub
'Overi, zda parametr M je retezec mesice
'a pokud ano, vrati jeho ciselnou hodnotu (jinak 0)
Function MonthToNumber (M As String) As Integer
Dim I As Integer
MonthToNumber = 0
For I = 1 To 12 If Months(I)=M Then MonthToNumber = I Exit Function
End If
Next I
End Function
'Otestuje, jestli v radku DataLine na pozici Position je datum
'pokud ano, vraci True a vysledne datum je ulozeno v globalni
'promenne CapturedDate
Function IsDateText (DataLine As String, Position As Integer) As Boolean
Dim Yr As Variant, Month As Variant, Day As Variant
'Nejprve otestujeme tvar celeho retezce
IsDateText = False
If Not Mid(DataLine, Position, 11) Like "[0-9][0-9].???.[1-9][0-9][0-9][0-9]"
Then Exit Function
End If
Month = MonthToNumber( Mid( DataLine, Position + 3, 3 ) )
If Month > 0 Then Day = Mid (DataLine, Position, 2) Yr = Mid (DataLine,
Position + 7, 4) CapturedDate = DateSerial( Day, Month, Yr ) IsDateText = True
End If
End Function

Sub SaveRecord( rsOut As Recordset ) rsOut.AddNew rsOut!Customer = Customer
rsOut!Subcontract = Subcontract rsOut!DueDate = DueDate rsOut!Amount = Amount
rsOut!TypeOfItem = TypeOfItem rsOut!Contact = Contact rsOut.Update
End Sub
Sub SaveRecord( fOut As Integer ) Print #fOut, Customer; Chr(9); Print #fOut,
Subcontract; Chr(9); Print #fOut, DueDate; Chr(9); Print #fOut, Amount; Chr(9);
Print #fOut, TypeOfItem; Chr(9); Print #fOut, Contact
End Sub
1: #!/usr/bin/perl
2: my ($ucet, $predcisli, $cis_polozky, $splatnost, $castka, $druh, $kontakt) ; 3: my @mesice = qw(JAN FEB MAR APR MAI JUN JUL AUG SEP OKT NOV DEZ) ; # zkratky
mesicu
4: my %mes = () ; #
5: for $i (0..11) { $mes{$mesice[$i]} = $i+1 } # pro rychly prevod mesic ->
cislo
6: while () { 7: s/x1B.//g ; # smazani ESC sekvenci
8: # detekce a extrakce prvniho radku:
9: if( /^ ((d{2}) )?((:?d{3}
)+)s+.+?sd{2}.[A-Z]{3}.d{4}s+-s+d{2}.[A-Z]{3}.d{4}/ ) {
10: ($ucet, $predcisli) = ($3, $2) ; 11: $ucet =~ s/ //g ; # vymaze vsechny mezery z identifikatoru uctu
12: }
13: # detekce a extrakce polozky: 14: if( s/^
(d+)s+((:?d+,)*d+.d+)(-?)s+/ ) {
15: ($cis_polozky, $castka, $sign, $splatnost, $druh, $kontakt)
16: = ($1, $2, $3, substr($_, 43, 11), substr($_, 56, 25), substr($_, 82)) ; 17: $castka =~ s/.//g ; $castka =~ s/,/./ ; # tecky pryc, misto desetinne
carky tecka
18: $castka = -$castka if $sign ; 19: $splatnost =~ /(d+).(w+).(d+)/ ; 20: $splatnost = $mes{$2} . "/$1/$3" ; 21: print join("t", $ucet, $predcisli, $cis_polozky, $splatnost, $castka,
$druh, $kontakt), "n" ; 22: }
23: }

Něco o Perlu...
PERL (prý "Practical Extraction and Reporting Language"), někdy též nazývaný
"Titanic" (protože kompletní instalační balík mívá několik MB, ačkoliv se
nejčastěji používá k programování drobných utilit). Na rozdíl od většiny
ostatních jazyků jej nevymyslel matematický inženýr, ale lingvista. Mottem
Perlu je TMTOWTDI (theres more than one way to do it, existuje více než [jen]
jeden způsob, jak to udělat) a také "aby šly jednoduché věci napsat jednoduše a
složité aby byly možné". V Perlu můžete napsat geniálně stručný a efektivní
program, ale také velmi stručný, nepřehledný a záhadný program, ve kterém se
nikdo nevyzná (ani vy) podle toho, čemu dáváte přednost. V Perlu můžete
prakticky cokoliv napsat objektově, ale nikdo vás k tomu nenutí. Pomocí
jednořádkového programu v Perlu můžete dát do pořádku textový soubor; existuje
ale i bankovní systém pro Relationship Management, který je z větší části
napsán v Perlu (spravuje přes 300 GB dat). Licenční politika Perlu je duální.
Perl je volně a bezplatně šiřitelný jednak pod GPL (Obecná veřejná licence, viz
http://www.gnu.org/ nebo http://www.gnu.cz/gplcz.html), jednak pod Artistic
License, která, zjednodušeně řečeno, umožňuje komerční (tedy úplatné) šíření
vlastních modifikací Perlu a aplikací napsaných v Perlu. ... a něco o Perlu pro
Windows
Z výše popsaného srovnání by se mohlo zdát, že Perl a podobné skriptovací
jazyky budou nepříjemně konkurovat Microsoftu, jehož Visual Basic byl donedávna
jediným slušnějším jazykem pro psaní skriptů v prostředí Windows a zejména
jediným široce dostupným nástrojem pro automatizaci v balíku Microsoft Office.
Některé čtenáře tedy možná překvapí (a jiné naopak nepřekvapí :-), že
nejpopulárnější port Perlu do Windows, vytvořený kanadskou firmou ActiveState
Tool Corporation, sponzoruje právě Microsoft na základě tříleté smlouvy o
spolupráci z roku 1999. ActivePerl již byl zahrnut do balíku unixových nástrojů
portovaných do Windows NT/2000, "Windows Services for Unix" verze 2.0.
Kromě ActivePerlu existují další windowsovské porty. Hyperlinky na ně najdete
na adrese http://www.cpan.org/ports/index.html#win32. IndigoStar
(http://www.indigostar.com/) je velmi zdařilá binární distribuce, která kromě
Perlu 5.6 obsahuje i webový server Apache s integrovaným modulem mod_perl
(umožňuje rychlé spouštění perlových CGI skriptů na Apachi). Tato sestava se
skvěle hodí pro vývoj a ladění webových aplikací, pokud jste z nějakého důvodu
nuceni pracovat na pracovní stanici s Windows a nemáte k dispozici potřebné
kompilační nástroje, např. Microsoft Visual Studio. Kromě toho je od IndigoStar
k dispozici program ke kompilaci perlových aplikací do souborů .exe.
Ke všem windowsovským portům Perlu existují moduly (knihovny) zpřístupňující
perlovým programům specifická aplikační rozhraní Windows, např. DDE, ODBC a
podobně. Podle mé osobní zkušenosti je nejspolehlivější ActivePerl, jediný
port, ve kterém mi bezchybně funguje ODBC.
Slovník pojmů
JPL: Java-Perl Lingo interface mezi Javou a Perlem
LWP: webová knihovna v Perlu
mod_perl: plug-in pro server Apache, který zabudovává Perl přímo do tohoto
webového serveru výsledkem je vysoká flexibilita a velmi dobrý výkon
perl: název jazyka (s velkým P), ale také program, který interpretuje kód v
Perlu a nechá jej vykonat počítačem.
1 0138 / pen









Komentáře
K tomuto článku není připojena žádná diskuze, nebo byla zakázána.