Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
-
5. září 2024
Matrixmedia - Obsluha a tisk na velkoformátových digitálních tiskárnách
-
30. září 2024
-
4. října 2024
Software
Skriptujeme InDesign (17): Regulární výrazy
Adobe InDesign sazba
23. června 2006, 00.00 | Velmi silný, ale ne zrovna snadno použitelný prostředek. Tak nějak bychom mohli popsat
použití regulárních výrazů při skriptování v InDesignu. Více už v našem článku.
Jak jsme již přislíbili, v této části našeho seriálu se budeme věnovat regulárním výrazům, prostředkům umožňující výrazně zefektivnit zpracování textového obsahu v InDesignu pomocí skriptů. Nicméně jak se z našeho výkladu záhy ukáže, nejedná se o nástroj, který by byl osvojitelný během chvilky.
Co jsou regulární výrazy?
Pod pojmem regulární výraz se rozumí výraz - vzor, dovolující popsat vlastnost určitého textového řetězce. Na základě tohoto popisu je možno regulární výraz použít jak při hledání, tak i nahrazování textového obsahu (a samozřejmě i dalších transformacích či jiném využití nalezeného obsahu, v případě InDesignu je to typicky formátování nalezených řetězců). Oproti funkci pro hledání a nahrazení v InDesignu, kterou jsme si popsali v minulých dílech našeho seriálu, jsou regulární výrazy podstatně univerzálnějším prostředkem a dovolují zapsat a následně tedy i zpracovat výrazy, pro jejichž vyjádření nemá daná vyhledávací funkce adekvátní prostředky. Daní placenou za tuto možnost je ovšem potřeba naučit se ne právě jednoduchá pravidla pro tvorbu regulárních výrazů a také použít určité skriptovací konstrukce, neboť přímočaré použití ve stylu výše popsané vyhledávací funkce InDesignu možné není - podpora regulárních výrazů je zde pro obecný JavaScript a její nasazení v rámci skriptování InDesignu je tak trochu navíc.
Základní pravidla pro tvorbu regulárních výrazů
V JavaScriptu existují dva způsoby, jak definovat regulární výraz. První je založen na použití výrazu - literáru ve tvaru:
/regulární_výraz/modifikátor
Syntaxi pro regulární_výraz probereme dále, nepovinné modifikátory mohou být g
(zpracovávají se všechny výskyty výrazu), i
(vyhledává se bez rozlišování malých a
velkých písmen) či m
(zpracování se týká víceřádkového režimu při použití značek ^
a $
-
viz další popis). Je-li použito modifikátorů více, mohou se psát vedle sebe bez ohledu
na pořadí.
Pokud tedy ve skriptu napíšeme:
var myRegVyr=/abc/gi;
definujeme tím proměnnou myRegVyr, která obsahuje regulární výraz abc
a je určena k
použití globálně a bez rozlišování malých a velkých písmen při hledání shody.
V druhém případě používáme konstruktoru RegExp
, vytvoření nového regulárního výrazu vypadá následovně
new RegExp("regulární_výraz", "modifikátory")
přičemž význam proměnných regulární_výraz a modifikátory je stejný jako v předchozím případě. Příklad použití (odpovídá významem předchozímu příkladu):
var myRegVyr=("abc","gi");
Pravidla pro zápis regulárních výrazů jsou poměrně komplikovaná a jejich podrobnější výklad jednak překračuje rozsah tohoto článku a navíc by zde byl značně samoúčelný. Podívejme se tedy na tomto místě pouze na základní konstrukty, které postačí v mnoha běžných situacích.
Za nejjednodušší typ regulárního výrazu lze považovat takový, který se skládá z
obvyklých znaků (písmena, čísla, oddělovače apod.). S takovýmto výrazem je shodný
řetězec obsahující přesně danou posloupnost znaků (např. abc
odpovídá řetězci abc).
Použít lze nicméně i tzv. ESC sekvence, s jejichž pomocí se zapisují některé znaky
speciální. Konkrétně to mohou být třeba \n
(nový řádek) či \t
(tabelátor). Dále lze pro
zápis znaku použít sekvenci \xnn
(znak, který má v ASCII pořadí nn,
přičemž nn je šestnáctkové číslo) či \uxxxx
(znak, který má v Unicode šestnáctkové pořadí
xxxx, např. pevnou mezeru zapíšeme jako \u00A0
, znak stupně jako \00B
) atp.). Některé
obvyklé znaky mohou mít při použití v regulárních výrazech speciální význam (hovoří se
pak o nich jako o metaznacích - viz dále), k vypnutí tohoto speciálního významu jim
předřadíme lomítko (tedy \\
značí znak lomítka, \*
značí hvězdičku, \+
značí plus atp.).
Na síle dodává regulárním výrazům možnost definovat tzv. třídy znaků. Ty určují nikoli
jen konkrétní znak, ale celou skupinu znaků splňující zadané kritérium. Konkrétně lze
takto použít zápis [znaky]
, který v hranatých závorkách určuje skupinu znaků do které
musí patřit znaky řetězce shodujícího se s daným regulárním výrazem. Skupinu znaků lze
přitom zapsat například s pomocí výčtu (např. [abc9]
znamená, že se řetězec může skládat
pouze se znaků a, b, c a 9) nebo intervalu (zápis [a-z0-9]
určuje, že se řetězec může
skládat pouze ze znaků v intervalu a-z a číslic od 0 do 9). Zápisem [^znaky]
pak naopak
určujeme skupinu znaků, které se v řetězci vyskytovat nesmí (takže [^0-9]
určuje, že se
v řetězci nesmí vyskytovat čísla). Znak tečky .
zastupuje libovolný jediný znak, vyjma
konce řádku. Sekvence \w
stanoví, že daná série znaků patří slovu (ekvivalent zápisu
[a-zA-Z0-9_]
), \W
naopak stanovuje, že znaky nepatří do slova. Zápis \s
určuje mezeru či
jiný "bílý" znak (tedy třeba i tabulátor, konec řádku atp.), \S
naopak libovolný znak,
který není mezera v daném smyslu. Číslici určuje zápis \d
(ekvivalent [0-9]
), znak, který
není číslicí pak znak \D
.
Znaky opakování, jinak též kvantifikátory, pak určují, kolikrát se shoda může či musí
opakovat. Metaznak ?
takto určuje jedno nebo žádné opakování, +
stanovuje, že se
předchozí výraz vyskytuje minimálně jedenkrát, *
pak, že předcházející výraz je nalezen
v libovolném počtu výskytů, včetně žádného. S pomocí zápisu {n}
lze určit, že se
předchozí výraz vyskytuje právě n-krát, v případě zápisu {n,}
právě n-krát nebo vícekrát
a {n,m}
pak nejméně n-krát a nejvíce m-krát.
U řetězců lze dále upřesnit jejich pozici, stanovující shodu. Znak ^
takto stanovuje, že
se shoda musí vyskytovat na začátku řetězce nebo (byl-li použit u regulárního výrazu
modifikátor m
pro víceřádkový režim - viz výklad výše) řádku. Obdobně znak $
určuje
shodu na konci řetězce či řádku. Výraz \b
znamená, že se shoda týká rozhraní mezi slovy,
v případě \B
pak naopak jakékoli jiné pozice. Výraz (?=vzor)
určuje, že následující
znaky musí odpovídat výrazu vzor, ale do výsledků se nezahrnují, (?!vzor)
naopak říká že
následující znaky vzoru nesmí odpovídat.
Konečně je ještě třeba zmínit znaky pro seskupování. Výraz a|b
říká, že shoda musí
nastat buďto s výrazem a nebo s výrazem b (např. výraz abc|bcd stanovuje shodu s jedním
z uvedených výrazů.). Uzavřením výrazu do závorek, tedy použitím vzoru (výraz)
, dojde k
tomu, že je daný výraz zapamatován k pozdějšímu použití jako tzv. podvýraz, a to pod
číslem, které odpovídá jeho pořadí ve výrazu. Dále je na něj možno odkazovat při
vyhledávání s pomocí zápisu \n
, kde n je číslo podvýrazu, či při nahrazování s pomocí
zápisu $n
, kde n je opět číslo skupiny. Takže například k vyhledání pětiznakového
palindromu (řetězce, jež se čte stejně z obou stran) použijeme výraz (.)(.)(.)$2$1
.
Tolik tedy v základu o syntaxi regulárních výrazů. Pro podrobnější výklad je zde k dispozici řada zdrojů, jmenujme takto alespoň referenční příručku JavaScriptu a seriál na serveru Interval. Je navíc vhodné zdůraznit, že pro mnoho situací se již někdo pokoušel regulární výraz vytvořit a i když nemusí být syntaxe takovéhoto výrazu stoprocentně spolehlivá a i když je pochopení či úprava regulárního výrazu mnohdy náročnější nežli vytvářet takovýto výraz zcela od základu, jmenujme několik zdrojů takovýchto výrazů. Konkrétně to tedy jsou třeba www.regularnivyrazy.info, Regexp.cz či RegExLib.
Nasazení regulárních výrazů v JavaScriptu
V rámci obecného JavaScriptu lze regulární výrazy použít v podstatě dvěma způsoby. První
vychází z odpovídajících metod objektu String
, a to především match
, search
a replace
(dále je k dispozici i metoda split
, ta pro nás v tuto chvíli nemá přílišný význam).
Match
má syntaxi
match(regulární_výraz)
a vyhledává v daném řetězci úseky odpovídající zadanému regulárnímu výrazu, jako
výsledek vrací pole s nalezenými výrazy. Pokud není nalezen ani jeden shodný úsek, je
výsledkem porovnání hodnota null
.
Metoda search
má opět syntaxi
search(regulární_výraz)
a vrací index prvního znaku prvního výskytu podřetězce odpovídajícího danému regulárnímu výrazu. Využití dané metody je pro náš účel obvykle malé.
Metoda replace
má syntaxi
replace(regulární_výraz,nahrazující_výraz)
Hodnota regulární_výraz zde přitom může být buďto regulárním výrazem nebo obyčejným
řetězcem uvedeným v uvozovkách. Nahrazující_výraz je buďto obyčejný řetězec nebo
řetězec, který může obsahovat některou z ESC sekvencí používaných v regulárních
výrazech, typicky odkaz na skupinu ($1
apod.).
Příklad:
var a="Josef Jan Josef Honza"
b=a.match(/Josef/g);
alert(b);
//vrací pole Josef,Josef
b=a.search(/Jan/);
alert(b);
//vrací hodnotu 6
var a="Josef Novák"
b=a.replace(/(Josef) (Novák)/,"$2, $1");
alert(b);
//vrací řetězec "Novák, Josef"
Druhý, pro naše použití obvykle méně důležitý případ, vychází z použití metod
regulárních výrazů samotných. Konkrétně je takto k dispozici metoda test
, která má zápis
test(řetězec)
a vrací hodnotu true, pokud řetězec obsahuje daný regulární výraz.
Příklad:
var a=/Honza/;
var string="Josef Jan Josef Honza"
b=a.test("Josef Jan Josef Honza");
alert(b) //zobrazí "true"
b=a.test("Josef");
alert(b) //zobrazí "false"
b=a.exec("Josef Jan Josef Honza");
alert(b) //zobrazí "false"
Dále je zde k dispozici metoda exec
, ve tvaru
exec(řetězec)
jež porovná zadaný řetězec s regulárním výrazem a vrátí pole s nalezenými výsledky nebo
null
, pokud nebylo nic nalezeno. Blíže se jí zde zabývat nebudeme.
Nasazení v InDesignu
V případě nasazení regulárních výrazů přímo při skriptování InDesignu se vyskytuje několik zádrhelů. Především se jedná o to, že jde o prostředek použitelný pro textový obsah, nikoli textové objekty. Změna v dokumentu (story, textovém rámečku atp.) není aplikovatelná přímo (existují možností jak to provést se změnami vlastnosti contents, ale to není vůbec elegantní řešení) a hlavně nedovoluje přímo měnit atributy textu. Obojí (tj. změny i formátování) dokáže měnit funkce hledání/nahrazování InDesignu, ta nicméně regulární výraz přímo jako svůj parametr neakceptuje. Existuje každopádně poměrně jednoduchý postup, jak obejít daný problém, který pro většinu aplikací regulárních výrazů v InDesignu postačí. Schématicky jej lze popsat následovně:
1. Zvolíme určitý textový objekt (story, textový rámeček, dokument, vývěr atp.), jenž chceme prohledávat a dále určíme jeho textový obsah (metoda contents).
2. Provedeme prohledání tohoto textového obsahu metodou match na výskyt daného regulárního výrazu. Získáme tím pole se všemi výskyty.
3. Pro jednotlivé výskyty regulárního výrazu spustíme funkci hledání a záměn InDesignu, kterou aplikujeme na daný textový objekt. To nám dovolí nejen provést náhrady, ale i využít podle libosti formátovací atributy. Při nahrazování přitom můžeme použít řetězce, které vzniknou náhradou v jednotlivých výskytech podle určitých regulárních výrazů.
Celý postup demonstruje následující skript:
//Skript, který v aktuálně vybrané story nalezne všechen
text
//ohraničený párovými tagy <b> a </b> (nebo
<B> a </B>),
//zformátuje jej znakovým stylem Tučný a tagy odstraní.
//Určíme textový objekt v kterém chceme provést náhradu
var myStory = app.selection[0].parentStory;
//Určíme text z tohoto objektu, který chceme prohledávat regulárním
výrazem
var myStoryText=myStory.contents;
//Do pole myFind vložíme všechny řetězce z prohledaného textu, které se
shodují se zadaným výrazem
var myFind =
myStory.contents.match(/<b>(.*?)<\/b>/gi);
//Bylo-li něco nalezeno, zpracováváme dále
if(myFind != null)
{
//Nulujeme preference vyhledávacího dialogu
app.findPreferences = app.changePreferences = null
//Cyklus pro všechny nalezené výskyty
for(i = 0; i<myFind.length; i++){
//Použijeme funkci pro hledání a záměny InDesignu,
provedeme zformátování stylem a náhradou odstraníme tagy
myStory.search(myFind[i],true,true,myFind[i].replace(/<b>(.*?)<\/b>/i,"$1"),{},{appliedCharacterStyle:
"Tučný"});
}
}
//Pokud se nic nenašlo, rovnou končíme s hlášením
else{alert("Nenalezen žádný řetězec k náhradě")};
Pokud bychom chtěli naznačený princip použít k sérii náhrad tagů a zformátování textu, který byl vytvořen v HTML editoru či exportem z nějaké aplikace (ale předpokládáme že korektně, tj. párové značky pracují dobře), můžeme přepsat algoritmus uvedený výše na funkci a pak jen použít sérii jejich volání s různými hodnotami:
//Funkce pro hledání a náhrady s pomocí regulárních výrazů
//Není univerzální!
//Parametry:
//myChSpace: objekt (prostor) ve kterém budeme hledat a zaměňovat -
dokument, story, textový výběr apod.
//myFSpace: text (prostor), který prohledáváme s pomocí regulárního
výrazu - obvykle získáme pomocí metody contents
//myFindString: regulární výraz, kterým prohledáváme myFSpace
//myChangeString: regulární výraz, kterým případně můžeme transformovat
myFindString
//myFindAt: atributy textu zpřesňující hledání v myChSpace, mají podobu
pole dvojic - viz předchozí díly tohoto seriálu
//myChangeAt: atributy textu zpřesňující náhrady v myChSpace, podoba
stejná jako u myFindAt
function
myRepRegEx(myChSpace,myFSpace,myFindString,myChangeString,myFindAt,myChangeAt)
{var myFind = myStoryText.match(myFindString);
alert(myFind)
if( myFind != null )
{
app.findPreferences = app.changePreferences = null
for( i = 0; i < myFind.length; i++ ){
//Dopracování vyžaduje nastavení druhého a třetého
parametru: zde necháváme rozlišovat malá/velká písmena a hranice slov
//V nalezených výsledcích transformujeme výsledek
záměny podle potřeb nastavením proměnné myChangeString
myChSpace.search(myFind[i], true, true,
myFind[i].replace(myFindString,myChangeString),myFindAt,myChangeAt);
}
}
else{alert("Nenalezen žádný řetězec k náhradě")};
}
//Volání dané funkce pro jednotlivé tagy
myRepRegEx(myStory,myStoryText,/<b>(.*?)<\/b>/gi,"$1",{},{appliedCharacterStyle:
"Tučný"});
myRepRegEx(myStory,myStoryText,/<i>(.*?)<\/i>/gi,"$1",{},{appliedCharacterStyle:
"Kurzíva"});
myRepRegEx(myStory,myStoryText,/<code>(.*?)<\/code>/gi,"$1",{},{appliedCharacterStyle:
"Kód"});
myRepRegEx(myStory,myStoryText,/<h4>(.*?)<\/h4>/gi,"$1",{},{appliedParagraphStyle:
"Nadpis 4"});
//atd.
Aplikovat jej lze i na text připravený původně pro TeX:
//Všechny výskyty řetězce uzavřeného značkami \emph{ a }
//tedy třeba \emph{Josef Novák}
//zformátuje znakovým stylem Index a značky odstraní.
//Obdobně lze zpracovat i rozsáhlé konstrukty z Plain TeXu, LaTeXu apod.
myRepRegEx(myStory,myStoryText,/\\emph\{([^}]+)}/g,"$1",{},{appliedCharacterStyle:
"Index"});
Dále se nabízí možnost formátování e-mailové adresy:
//Nejjednodušší forma, dovoluje použít i různé nesprávné
adresy, ale validita je zde přece obvykle na tvůrci podkladu
myRepRegEx(myStory,myStoryText,/([a-zA-Z_.]+@[a-zA-Z_.]+)/g,"$1",{},{appliedCharacterStyle:
"E-mail"});
//Zde zpracujeme jen e-mailové adresy končící na .cz
myRepRegEx(myStory,myStoryText,/([a-zA-Z_.]+@[a-zA-Z_.]+\.cz)/g,"$1",{},{appliedCharacterStyle:
"E-mail"});
či třeba webového odkazu
//Zřejmě nejjednodušší tvar webového odkazu, existuje mnoho přesnějších způsobů zápisu
myRepRegEx(myStory,myStoryText,/(http:\/\/[a-zA-Z_.]+)/gi,"$1",{},{appliedCharacterStyle: "HTTP"});
Pokud máme dokument, ve kterém jsou pěticiferná čísla použita pro PSČ a chceme nesprávný zápis ve tvaru XXXXX nahradit zápisem ve tvaru XXXpevná_mezeraXX, můžeme tak učinit s pomocí následující náhrady:
myRepRegEx(myStory,myStoryText,/\b(\d{3})(\d{2})\b/gi,"$1\u00A0$2",{},{});
Možností, jak aplikovat regulární výrazy, je nepřeberné množství. Zde jsme si ukázali několik málo příkladů náhrad a formátování, ale mnoho aplikací může mít i prosté vyhledávání, jež nám dovolí rychle určit, zda se v dokumentu či jeho části vyskytuje určitý typ řetězce.
Jak si pak lze z našich příkladů i externích zdrojů povšimnout, vytvořit opravdu univerzální, "neprůstřelný" regulární výraz nemusí být vždy úplně snadné. Mnohdy je ale lepší pokusit se napsat něco jednoduššího, co bude fungovat ve většině případů, nežli strávit hledáním vhodného vyjádření více času, než kdybychom mezitím text upravili běžnými prostředky.
Otázkou pak zůstává, jak skriptu předat požadovaný textový rozsah k prohledávání. Mimo triviálních možností (použitých výše) bývá obvyklý požadavek na zjištění veškerého textu v dokumentu. Zde nám může pomoci třeba následující postup:
//Proměnnou myText naplníme obsahem všech story dokumentu
var myDoc=app.activeDocument;
var myText = '';
for(i = 0; myDoc.stories.length > i; i++ ){myText +=
myDoc.stories[i].contents + ''};
Obsah seriálu (více o seriálu):
- Skriptujeme InDesign (1): Úvod
- Skriptujeme InDesign (2): První kroky
- Skriptujeme InDesign (3): Základy JavaScriptu poprvé
- Skriptujeme InDesign (4): Základy JavaScriptu podruhé
- Skriptujeme InDesign (5): Dokumenty poprvé
- Skriptujeme InDesign (6): Dokumenty podruhé
- Skriptujeme InDesign (7): Výstup
- Skriptujeme InDesign (8): Stránky
- Skriptujeme InDesign (9): Stránkové objekty poprvé
- Skriptujeme InDesign (10): Stránkové objekty podruhé
- Skriptujeme InDesign (11): Dialogy
- Skriptujeme InDesign (12): Text poprvé
- Skriptujeme InDesign (13): Text podruhé
- Skriptujeme InDesign (14): Text potřetí
- Skriptujeme InDesign (15): Hledání a záměny poprvé
- Skriptujeme InDesign (16): Hledání a záměny podruhé
- Skriptujeme InDesign (17): Regulární výrazy
- Skriptujeme InDesign (18): Tabulky poprvé
- Skriptujeme InDesign (19): Tabulky podruhé
- Skriptujeme InDesign (20): Obrázky poprvé
- Skriptujeme InDesign (21): Obrázky podruhé
- Skriptujeme InDesign (22): Obrázky potřetí
- Skriptujeme InDesign (23): ESTK aneb jak na editaci a ladění skriptů
- Skriptujeme InDesign (24): Vrstvy
- Skriptujeme InDesign (25): Barvy poprvé
- Skriptujeme InDesign (26): Barvy podruhé
- Skriptujeme InDesign (27): Barvy potřetí
- Skriptujeme InDesign (29): Metadata podruhé
- Skriptujeme InDesign (30): Návěští
- Skriptujeme InDesign (31): Okna
- Skriptujeme InDesign (32): Reakce na události
- Skriptujeme InDesign (33): Novinky v CS3 verzi
Tématické zařazení:
» Rubriky » Polygrafie
-
14. května 2014
Jak vkládat snímky do galerií a soutěží? Stručný obrazový průvodce
-
23. dubna 2014
Konica Minolta přenesla výhody velkých zařízení do kompaktních modelů
-
12. června 2012
-
9. dubna 2014
-
29. listopadu 2013
-
6. září 2004
OKI snižuje ceny barevných laserových tiskáren C3100 a C5200n
-
13. května 2004
-
19. ledna 2004
QuarkXPress Passport 6: předvedení nové verze na konferenci Apple Forum 27.1.2004
-
6. února 2001
-
30. listopadu 2014
Nový fotoaparát α7 II: první plnoformát s pětiosou optickou stabilizací obrazu na světě
-
5. srpna 2024
Bubnový scanner na 4000dpi optické rozlišení + PC + software
-
8. září 2024
-
14. října 2024
-
5. listopadu 2024