DVD titulky do TXT – sed a regulární výrazy

Převod titulků z DVD – The Final Countdown

Tímto navazuji na můj předchozí článek o převodu titulků z DVD to TXT. Zůstalatam totiž nedokončena pasáž týkající se ‘automatických’ oprav chyb, které bohužel vždyvznikají při aplikaci OCR software. I zde si na pomoc vezmu proudový editor sed, který je součástí snad každédistribuce GNU/Linuxu.


sed je velmi šikovný program používaný z příkazové řádky a z toho vyplývá jeho síla – je neskutečněrychlý a efektivní. Jeho možnosti jsou rozsáhlé, my využijeme pouze funkci nahrazování za použití rugulárních výrazů. Tojsou hrůzostrašně vyhlížející konstrukce, změť normálních a zpěrných lomítek. Myslel jsem si, že je nikdy neovládnu. Alepři psaní toho článku jsem se je učil "za pochodu" a radikálně jsem změnil názor. Najednou jsem se snažil vytvořit co nejkrkolomnějšía nejsložitější regulární výraz, který by ale byl pochopitelně funkční. Kolikrát mě překvapilo, že skript postupněumí i věci, které jsem původně ani nezamýšlel :o))

Můžete si stáhnout skript (sed_cz_iso2) pro sed, ať to nemusíte pořád vypisovat.Jeho použití je pak:

  sed -f sed_cz_iso2 < Puvodni_titulky > Nove_titulky

Formát souboru Puvodni_titulky musí být v kódování ISO-8859-2 a nejlépe ve formátu SRT tedy tak, jak vzejdoupo použití programu dvdsub z projektu Martina Kačera.Doporučuju používat pouze na české titulky, je tamněkolik specifik pro češtinu. Inspiraci jsem čerpal z projektu Martina Kačera, ze zdrojáků SubRipu a něco jsemsi dovolil vymyslet sám. Taky jsem si v průběhu vymýšlení a ladění formy výrazů a jejich pořadí vytvořiltestovací soubor s různými "chuťovkami".

POZOR! Skript se rovněž pokouší řešit nepříjemný problém, který vzniká proto, že velké I vypadá u DVD titulkůstejně jako malé L. Aby to bylo jednodušší, provádí se jen kontrola a nahrazování ve směru z malého L na velké I. Protouž při převodu titulků-obrázků na ASCII znaky (např. pomocí dvdsub) je nutno zajistit, aby všude bylo malé L. Tzn. že pokudse vás OCR program nejdříve zeptá na rozpoznané velké I, odpovězte mu, že je to malé L, jinak budete mít všude jen velké I a tose na malé L převádí o poznání hůře (resp. byste nejdříve museli změnit všechna velká I na malá L – což je jeden příkazv sedu – a teprve pak použít můj skript). Velké I s čárkou samozřejmě při OCR převodu nečiní potíže.

A jdeme na to!!! (sed -e ‘s/d/eb/g’)

Teď si probereme jednotlivé položky skriptu pro sed – využijeme zde síly regulárních výrazů(více o nich se dozvíte např. v článcích Pavla Satrapy). Jednaz možností využití programu sed je nahrazování, jež má tvar např. s/hledej/nahraď/g, kde/g znamená, že se má prohledávat celý vstup (global) od začátku do konce, nikoli jen jediný výskyt.

  • s/''/"/g
    nahrazení dvou apostrofů jedněmi uvozovkami; při běhu programu dvdsub si dávejte pozor, abyste používali vždy apostrof anglické klávesnice (na klávese pod uvozovkami), nikoli čárku nad písmeny či zpětný apostrof nebo český apostrof, ušetříte si tak spoustu práce
  • s/'"/"/g
    odstranění drobných problémů s uvozovkami a apostrofy
  • s/"'/"/g
    odstranění drobných problémů s uvozovkami a apostrofy
  • s/\ \+"/"/g
    hledáme mezery před uvozovkami a tyto dva a více znaků nahradíme jediným – jedněmi uvozovkami; jelikož znak mezera (ASCII 32) má svůj specifický význam – oddělovač argumentů, musíme tento její význam potlačit opačným lomítkem \ a v případě symbolu + je tomu naopak – abychom zapnuli jeho specifický význam (opakování předcházejícího znaku – zde mezery – alespoň jednou), musíme použít zpětné lomítko; některé programy používající klasické regulární výrazy (grep, sed, vi) zpětným lomítkem zapínají speciální významy některých znaků, jiné programy pracující s rozšířenými regulárními výrazy naopak zpětným lomítkem speciální významy některých znaků vypínají – takže Perl, awk, egrep používají jen + a sed aj. zase vyžadují \+ pro jejich speciální význam. Ach ta standardizace :o)
  • s/"\ \+/"/g
    vynechání všech mezer za uvozovkami, resp. nahrazení uvozovek a všech mezer za nimi jedním znakem uvozovek
  • s/\("[^"]*"\)/\ \1\ /g
    tahle konstrukce se mi povedla :o) dělá to, že před a za výraz_v_uvozovkách vloží mezery; konstrukce \( a \) se používá pro zapamatování obsahu v závorkách – sed opět požaduje uvození závorek zpětným lomítkem kdežto Perl nikoli; na zapamatovaný obsah se pak odkazujeme pomocí \číslo zde \1. Zapamatování lze řetězit či vnořovat a číslo odkazu se počítá podle pořadí levé závorky zleva. Konstrukce [] označuje výčet – [ast] znamená právě jedno z písmen a, s nebo t. Lze i negovat pomocí stříšky ^ jako v tomto případě – [^”] znamená cokoli kromě uvozovek. Opakování znaků zajišťuje hvězdička, tečka bez zpětného lomítka znamená jeden libovolný znak. Regulární výrazy se vyznačují svou nenasytností, tzn. že se snaží najít co možná nejdelší výraz, který odpovídá vzoru v rámci jednoho řádku. Kdybychom hledali výraz “*.” tak by výsledek mohl být i “text1″,”text2” tedy uvozovky v uvozovkách, což však nechceme. Proto hledáme uvozovky následované ostatními znaky kromě uvozovek a uvozovkami. Tento nalezený a zapamatovaný řetězec pak doplníme před a za jednou mezerou.
  • s/^\ \+//g
    vynechání všech mezer na začátku řádku; stříška, pokud není ve výčtu jako negace, označuje začátek řádku
  • s/""/"/g
    odstranění zdvojených uvozovek
  • s/\ \+,/,/g
    odstranění všech mezer před čárkou
  • s/\ \+\./\./g
    odstranění všech mezer před tečkou; tečka má v regulárních výrazech svůj speciální význam – jeden libovolný znak – a tak ho musíme vypnout opačným lomítkem
  • s/\ \+?/?/g
    vynechání všech mezer před otazníkem; otazník má v regulárních výrazech také svůj specifický význam – označuje nejvýše jeden výskyt předcházejícího znaku; v sedu bychom museli tuto vlastnost aktivovat zpětným lomítkem obdobně jako u +
  • s/\ \+!/!/g
    vynechání všech mezer před vykřičníkem
  • s/\([[:digit:]]\)\ \([[:digit:]]\)/\1\2/g
    vynechání mezery mezi číslicemi – nenapadá mě případ, kdy by číslice měly být odděleny mezerou (kromě řádů a tel. čísel)
  • s/\([[:digit:]]\)\ \([[:digit:]]\)/\1\2/g
    zopakování předchozího kroku – v případě vzorku 1 2 3 4 by totiž napoprvé došlo k odstranění jen každé druhé mezery, protože sed prochází soubor neprůnikově, tzn. že nejprve vyhoví 1 2 a pak se posune na mezeru a další vyhoví 3 4. Až v druhém kole najde 2 3
  • s/,\.\.\./\.\.\./g
    záměna ,… za …
  • s/?\./?/g
    odstranění tečky za otazníkem
  • s/!\./!/g
    odstranění tečky za vykřičníkem
  • s/,\([^[:digit:]"|]\)/,\ \1/g
    doplnění mezery za čárku, pokud za ní nenásleduje číslice, uvozovky nebo svislá čára – hlavně z důvodu lepšího automatického zalamování dlouhých titulků na více řádků; pokud za čárkou už mezera je, pak přibude další – to pak vyřeší jeden z následujících příkazů (odstranění duplicitních mezer); svislou čáru používá formát titulků MicroDVD SUB v rámci jednoho řádku k rozdělení víceřádkových titulků pro zobrazení
  • s/;/;\ /g
    doplnění mezery za
    středník – viz výše; zde mohou vyvstat potíže u SUB formátu titulků – jejich rozšířený formát totiž umožňuje označovat, které titulky mají být kurzívou či tučné, a při tom používá jako oddělovač parametrů ve složených závorkách právě středník; to se ovšem týká jen některých případů, kdy pustíte skript dodatečně na titulky vytvořené jinak, než popisuji já
  • s/:\([^[:digit:]]\)/:\ \1/g
    doplnění mezery za dvojtečku, pokud nenásleduje číslice
  • s/?\([^?!"|)]\)/?\ \1/g
    doplnění mezery za otazník, pokud nenásleduje otazník, vykřičník, uvozovky, svislá čára nebo závorka
  • s/!\([^?!"|)]\)/!\ \1/g
    doplnění mezery za vykřičník, pokud nenásleduje otazník, vykřičník, uvozovky, svislá čára nebo závorka
  • s/"\ "\([^[:alnum:]]\)/"\1/g
    nahrazení mezery v uvozovkách (zdvojených uvozovek oddělených mezerou) jedněmi uvozovkami, pokud za nimi bezprostředně nenásleduje písmeno nebo číslo – [:alnum:]
  • s/\ \+/\ /g
    náhrada více mezer za sebou jen jednou mezerou
  • s/d'/ď/g
    změna d a apostrofu na d s háčkem
  • s/t'/ť/g
    změna t a apostrofu na t s háčkem
  • s/\<lhned/Ihned/g
    Nyní následuje řada úprav pro češtinu, kdy se malé L převádí na velké I; zde slovo ‘ihned’ na začátku věty
  • s/\<lk\([^aá]\)/Ik\1/g
    všechna slova začínající na ‘lk’ vyjma těch začínajících ‘lka’ a ‘lká’ budou změněna na ‘Ik’
  • s/\<lkar/Ikar/g
    z předchozí výjimky pro ‘lka’ vyjmeme slova Ikar- (Ikasor, Ikarus, Ikarie aj.)
  • s/\<ln\([^ouě]\)/In\1/g
    slova začínající na ‘ln’ kromě těch na ‘lno’, ‘lnu’, ‘lně’ budou změněny na ‘In’
  • s/\<lnov/Inov/g
    slova začínající na ‘lnov’ změnit na ‘Inov’ (např. slovo ‘inovace’ na začátku věty)
  • s/\<lnui/Inui/g
    slova ‘inulin’ a ‘inundace’ na začátku věty :o)
  • s/\<lont/Iont/g
    různé tvary kořene ‘iont’ na začátku věty; na žádné slovo na ‘lont’ jsem si nevzpomněl :o/
  • s/\<loniz/Ioniz/g
    např. ‘ionizační’ na začátku věty
  • s/\<lono/Iono/g
    např. ‘ionosféra’ na začátku věty
  • s/\<lpsa/Ipsa/g
    slovo ‘ipsace’ na začátku věty :o) (todle si fakt najděte ve slovníku…)
  • s/\<lpeka/Ipeka/g
    slovo ‘ipekakuanha’ na začátku věty :o)
  • s/\<lQ/IQ/g
    každý máme nějaké to IQ…
  • s/\<ls\([^t]\)/Is\1/g
    slova začínající na ‘ls’ kromě těch na ‘lst’ (lstivý, lstivě, aj.) změnit na ‘Is’
  • s/\<l\([bcdfgjmrtwxz]\)/I\1/g
    předpokládám, že nejsou slova, která by začínala na lb, lc, ld, lf, …
  • s/\<Ize\>/lze/g
    oprava předchozího příkazu, kdy jediné slovo na ‘lz’ – lze – bylo mylně změněno
  • s/\<lan\>/Ian/g
    oprava počátečního velkého I ve jménu Ian – pády nelze jednoduše testovat, neboť má smysl i ‘lana’
  • s/\<lvan/Ivan/g
    oprava počátečního velkého I ve jménu Ivan a všech jeho tvarech
  • s/\<lvet/Ivet/g
    oprava počátečního velkého I ve jménu Iveta a všech jeho tvarech
  • s/\<lvo\>/Ivo/g
    oprava počátečního velkého I ve jménu Ivo; tvary kolidují se slovy lev/lví
  • s/\<lvon/Ivon/g
    oprava počátečního velkého I ve jménu Ivon(a) a všech jeho tvarech
  • s/\<lvoš/Ivoš/g
    oprava počátečního velkého I ve jménu Ivoš a všech jeho tvarech
  • s/\<l\>/I/g
    kontrola osamoceného velkého I nebo římské 1; výraz \< označuje začátek slova, výraz \> pak konec slova; slovo je ohraničeno mezerou, čárkou, tečkou, vykřičníkem, otazníkem…
  • s/<Iž/lž/g
    oprava předchozího příkazu, kdy ‘ž’ se považovalo za hranici slova (chyba v lokalizačních proměnných?) a u slov lže, lžíce aj. bylo počáteční malé L nahrazeno velkým I
  • s/n\.I\./n\.l\./g
    předchozí řádek také špatně opravil zkratku n.l. (př.n.l.) na n.I. tak je to třeba vrátit zpět na n.l.
  • s/\<ll\>/II/g
    kontrola osamocené římské číslice 2
  • s/\<lll\>/III/g
    kontrola osamocené římské číslice 3; pořadí řádků pro záměnu malého L za velké I není libovolná, alespoň při podobě příkazů, jak je uvádím zde – trojité malé L je totiž nejdříve testováno, zda je to římská 3, až pokud tomu tak není, pak dojde ke změně pouze prvního malého L na velké I; pokud by byly řádky přehozeny, pak by došlo k záměně lll za Ill a test na římskou trojku by už byl neúspěšný; samozřejmě by se pořadí dalo ‘znecitlivit’ složitější konstrukcí některých výrazů
  • s/Vll\>/VII/g
    kontrola římské číslice 7 ať už osamocené či na konci většího čísla
  • s/Vlll\>/VIII/g
    kontrola římské číslice 8 ať už osamocené či na konci většího čísla
  • s/Xll\>/VII/g
    kontrola římské číslice 12 ať už osamocené či na konci většího čísla
  • s/Xlll\>/XIII/g
    kontrola římské číslice 13 ať už osamocené či na konci většího čísla
  • s/l\([[:upper:]]\)/I\1/g
    konstrukci [[:upper:]] vyhoví jakékoli velké písmeno – podle nastavení lokalizačních proměnných též velká česká písmena s diakritikou; tento příkaz nahradí malé L velkým I všude tam, kde je malé L následováno libovolným velkým znakem; pokud máte problémy s tím, že nejsou &quotvnímána&quot velká písmena s diakritikou, zkuste konstrukci [[:upper:]ÁČĎÉĚÍÓŘŠŤÚŮÝŽ]
  • s/\([[:upper:]]\)l\>/\1I/g
    tímto příkazem se nahradí malé L velkým I, pokud je malé L na konci slova – konstrukce /> – a před ním je velké písmeno
  • s/\<ll/Il/g
    jestliže slovo začíná na ‘ll’, je první písmeno změněno na velké I
  • s/\.\ l/\.\ I/g
    velké I na začátku věty (tečka, mezera, malé L -> velké I)
  • Ručně je nutno projít slova lva/lvu/lana, jestli nemají být spíše Iva/Ivu/Iana

Jsem si jist, že tento skript nemůže stoprocentně opravit chyby po OCR software, ale myslím si, že po něm bude kvalitatitulků již tak vysoká, že není třeba je procházet "ručně". Samozřejmě vítám každý návrh na vylepšení – jistěse setkáte s titulky, na které bude jakýkoli natož tento automatizovaný skript krátký.

Případné aktualizace skriptu by se objevily na mojí stránce.

1 komentář

  1. hanus | 05.08.2003 | 15:15 | Odpovědět

    !!! POZOR CHYBA !!!
    V textu článku je to dobře, ale ve skriptu ke stažení je chyba na 11.řádku odspodu. Nemá tam být
    s/n.I./n.l./g
    ale
    s/n.I./n.l./g
    Správný skript ke stažení je na mé www:
    http://hanus.host.sk/linux/linux.htm
    v příslušném článku. Omlouvám se za chybu.

Leave a comment

Sorry, you must be logged in to post a comment. Login