8.4 Funkce pro práci s regulárními výrazy
Všechny funkce pro práci s regulárními výrazy v balíku stringr mají stejný interface:
str_jméno(řetězec, vzor)
kde řetězec
je vektor zpracovávaných řetězců a vzor
je použitý regulární výraz (některé funkce mají i další parametry). Jména všech těchto funkcí začínají na str_
. Pokud některá funkce vrací je první výskyt hledaného vzoru, pak odpovídají funkce končící na _all
vrací všechny výskytu tohoto vzoru.
8.4.1 Detekce vzoru
K detekci, zda řetězec obsahuje zvolený regulární výraz, slouží funkce str_detect(s, p)
, kde s
je prohledávaný vektor řetězců a p
je hledaný vzor. Funkce vrací logický vektor stejné délky jako s
s hodnotou TRUE
, pokud byl vzor nalezen, a hodnotou FALSE
v opačném případě. Příklady viz výše.
8.4.2 Výběr řetězců, které odpovídají vzoru
Funkce str_subset(s, p)
vrací ty prvky vektoru s
, které obsahují vzor p
. Je vlastně ekvivalentní výrazu
x[str_detect(x, p)]
Řekněme, že chceme vybrat ty řádky, které obsahují telefonní čísla:
r <- "(\\+420)?[-\\s]*\\d{3}[-\\s]*\\d{3}[-\\s]*\\d{3}"
cisla <- c("Leoš: 777 666 555 444",
"Lída: domů +420 734 123 456, práce 777-666-555",
"Leona: nevím")
str_subset(cisla, r)
## [1] "Leoš: 777 666 555 444"
## [2] "Lída: domů +420 734 123 456, práce 777-666-555"
8.4.3 Počet výskytů vzoru v řetězci
Funkce str_count(s, p)
spočítá počet výskytů regulárního výrazu p
v řetězci s
. Pracuje vektorově přes zadaný vektor řetězců s
i přes regulární výraz p
. Implicitní hodnota p
je prázdný řetězec ""
. V tomto případě vrací funkce str_count()
počet znaků v řetězci (počítaných podle aktuálního locale).
ovoce <- c("jablo", "ananas", "hruška", "rybíz")
str_count(ovoce, "a") # počet výskytů "a" v každém slově
## [1] 1 3 1 0
# počet "a" v jablku, "a" v ananasu, "b" v hrušce a "r" v rybízu
str_count(ovoce, c("a", "a", "b", "r"))
## [1] 1 3 0 1
str_count(c("Ahoj", "lidičky!"), "") # počet znaků
## [1] 4 8
8.4.4 Získání částí řetězců, které splnují vzor
Funkce str_extract(s, p)
získá z každého řetězce ve vektoru s
tu jeho část, která odpovídá prvnímu výskytu vzoru p
. Funkce vrací vektor stejné délky jako s
; pokud není vzor nalezen, vrací NA
.
Výběr klíčových slov z tweetů:
r <- "#[[:alpha:]]+" # hashtag následovaný jedním nebo více písmeny
tweet <- c("#Brno je prostě nejlepší a #MU je nejlepší v Brně.",
"Někdy je možné najít zajímavé podcasty na #LSE.",
"Stupnování 'divnosti': divný, divnější, #ParisHilton.",
"Docela prázdný tweet.")
str_extract(tweet, r)
## [1] "#Brno" "#LSE" "#ParisHilton" NA
Funkce str_extract_all(s, p, simplify)
vrátí všechny výskytu tohoto vzoru. Pokud není parametr simplify
zadán nebo je FALSE
, vrací funkce seznam, jehož prvky odpovídají prvkům vektoru s
; nenalezené výskyty jsou pak prázdný vektor řetězců (character(0)
). Pokud je simplify = TRUE
, pak vrací matici, jejíž řádky odpovídají prvkům vektoru s
; nenalezené výskyty jsou pak označené jako prázdné řetězce ""
.
str_extract_all(tweet, r)
## [[1]]
## [1] "#Brno" "#MU"
##
## [[2]]
## [1] "#LSE"
##
## [[3]]
## [1] "#ParisHilton"
##
## [[4]]
## character(0)
str_extract_all(tweet, r, simplify = TRUE)
## [,1] [,2]
## [1,] "#Brno" "#MU"
## [2,] "#LSE" ""
## [3,] "#ParisHilton" ""
## [4,] "" ""
Někdy nechceme získat celý vzor, nýbrž pouze jeho části. K tomu slouží funkce str_match(s, p)
a str_match_all(s, p)
, kde s
je vektor prohledávaných řetězců a p
regulární výraz. K rozdělení regulárního výrazu do částí se používají skupiny.
Funkce str_match()
hledá první výskyt regulárního výrazu p
v řetězci a vrací matici, jejíž řádky odpovídají prvkům vektoru s
. První sloupec je celý regulární výraz, druhý sloupec první skupina v regulárním výrazu, třetí sloupec druhá skupina atd. (Skupiny mohou být zanořené. Jejich pořadí se počítá podle pořadí levé závorky.) Nenalezené prvky mají hodnotu NA
.
Pokud např. chceme získat jméno odkazovaného předmětu bez hashtagu, můžeme vzít druhý sloupec následující matice:
r <- "#([[:alpha:]]+)" # totéž, co výše, ale všechna písmena tvoří skupinu
str_match(tweet, r)
## [,1] [,2]
## [1,] "#Brno" "Brno"
## [2,] "#LSE" "LSE"
## [3,] "#ParisHilton" "ParisHilton"
## [4,] NA NA
Funkce str_match_all()
vrací všechny výskyty regulárního výrazu. Jejím výsledkem je seznam matic. Rádky těchto matic odpovídají jednotlivým nálezům. Sloupce mají význam jako výše. Nenalezené prvky mají v tomto případě hodnotu character(0)
.
str_match_all(tweet, r)
## [[1]]
## [,1] [,2]
## [1,] "#Brno" "Brno"
## [2,] "#MU" "MU"
##
## [[2]]
## [,1] [,2]
## [1,] "#LSE" "LSE"
##
## [[3]]
## [,1] [,2]
## [1,] "#ParisHilton" "ParisHilton"
##
## [[4]]
## [,1] [,2]
Příklad: převod českého telefonního čísla do standardního tvaru (budeme brát jen první číslo pro každého člověka):
r <- "(\\+420)?[-\\s]*(\\d{3})[-\\s]*(\\d{3})[-\\s]*(\\d{3})"
cisla <- c("Leoš: 777 666 555 444",
"Lída: domů +420 734 123 456, práce 777-666-555",
"Leona: nevím")
cisla2 <- str_match(cisla, r)[, 3:5]
apply(cisla2, 1, function(x) str_c(x, collapse = "-"))
## [1] "777-666-555" "734-123-456" NA
8.4.5 Indexy řetězců splnujících vzor
Někdy se hodí najít indexy, kde začíná a končí vzor v daném řetězci. K tomu slouží funkce str_locate()
a str_locate_all
. Funkce str_locate(s, p)
najde indexy prvního výskytu regulárního výrazu p
v řetězci s
. Výsledek je matice, jejíž řádky odpovídají prvkům vektoru s
. První sloupce je index začátku prvního výskytu vzoru, druhý sloupec je index konce prvního výskytu vzoru. Pokud není vzor v řetězci nalezen, vrátí NA
:
r <- "#[[:alpha:]]+" # hashtag následovaný jedním nebo více písmeny
tweet <- c("#Brno je prostě nejlepší a #MU je nejlepší v Brně.",
"Někdy je možné najít zajímavé podcasty na #LSE.",
"Stupnování 'divnosti': divný, divnější, #ParisHilton.",
"Docela prázdný tweet.")
str_locate(tweet, r) # vrací matici indexů prvních výskytů klíčových slov v tweetu
## start end
## [1,] 1 5
## [2,] 43 46
## [3,] 41 52
## [4,] NA NA
str_sub(tweet, str_locate(tweet, r)) # vyřízne tato klíčová slova
## [1] "#Brno" "#LSE" "#ParisHilton" NA
str_extract(tweet, r) # totéž
## [1] "#Brno" "#LSE" "#ParisHilton" NA
K nalezení všech výskytů vzoru ve vektoru řetězců s
slouží funkce str_locate_all(s, p)
, která vrací seznam matic indexů. Prvky seznamu odpovídají prvkům vektoru s
. Rádky každé matice odpovídají jednotlivým výskytům vzoru v jednom prvku vektoru s
. První sloupec matice je index začátku výskytu, druhý sloupec je index konce výskytu. Pokud není vzor nalezen, vrací matici s nula řádky:
str_locate_all(tweet, r)
## [[1]]
## start end
## [1,] 1 5
## [2,] 28 30
##
## [[2]]
## start end
## [1,] 43 46
##
## [[3]]
## start end
## [1,] 41 52
##
## [[4]]
## start end
Funkce invert_match(loc)
invertuje matici indexů vrácenou funkcí str_locate_all()
, takže obsahuje indexy částí řetězce, které neodpovídají vzoru. Detaily viz dokumentace.
8.4.6 Nahrazení vzoru
K nahrazení vzoru ve vektoru řetězců slouží funkce str_replace()
a str_replace_all()
. Funkce str_replace(s, p, r)
nahradí ve vektoru řetězců s
první výskyt vzoru p
řetězcem r
. Funkce str_replace_all()
má stejnou syntaxi, ale nahradí všechny výskyty vzoru ve vektoru s
.
Řekněme, že chceme v každém tweetu nahradit všechna klíčová slova řetězcem `“XXX”:
r <- "#[[:alpha:]]+"
str_replace_all(tweet, r, "#XXX")
## [1] "#XXX je prostě nejlepší a #XXX je nejlepší v Brně."
## [2] "Někdy je možné najít zajímavé podcasty na #XXX."
## [3] "Stupnování 'divnosti': divný, divnější, #XXX."
## [4] "Docela prázdný tweet."
Klíčové slovo vymažeme tak, že je nahradíme prázdným řetězcem ""
:
str_replace_all(tweet, r, "")
## [1] " je prostě nejlepší a je nejlepší v Brně."
## [2] "Někdy je možné najít zajímavé podcasty na ."
## [3] "Stupnování 'divnosti': divný, divnější, ."
## [4] "Docela prázdný tweet."
Funkce str_replace_all()
dokáže nahradit více vzorů naráz. K tomu stačí nahradit vzor p
a nahrazující řetězec r
vektorem řetězců: jména prvků vektorů určují, co se nahrazuje, hodnoty určují, čím se nahrazuje:
ovoce <- c("jeden banán", "dva pomeranče", "tři mandarinky")
str_replace_all(ovoce,
c("jeden" = "1", "dva" = "2", "tři" = "3"))
## [1] "1 banán" "2 pomeranče" "3 mandarinky"
Obě funkce si zapamatují obsah skupin obsažených v regulárním výrazu a mohou jej použít v nahrazujícím řetězci r
. Obsah první skupiny je \1
, druhé skupiny \2
atd. (v R je ovšem třeba zpětné lomítko zdvojit). Řekněme, že chceme zdvojit klíčová slova v tweetech upravit tak, že slovo bude nejdříve uvedeno bez hashtagu, a pak v závorce s hashtagem:
r <- "#([[:alpha:]]+)"
str_replace_all(tweet, r, "\\1 (#\\1)")
## [1] "Brno (#Brno) je prostě nejlepší a MU (#MU) je nejlepší v Brně."
## [2] "Někdy je možné najít zajímavé podcasty na LSE (#LSE)."
## [3] "Stupnování 'divnosti': divný, divnější, ParisHilton (#ParisHilton)."
## [4] "Docela prázdný tweet."
Příklad: máte vektor datumů v anglosaském formátu, tj. “MM-DD-YYYY”, a chcete je převést do českého formátu “DD. MM. YYYY”:
datumy <- c("born: 06-01-2001", "died: 02-01-2017", "no information at all")
str_replace_all(datumy, "(\\d{2})-(\\d{2})-(\\d{4})", "\\2. \\1. \\3")
## [1] "born: 01. 06. 2001" "died: 01. 02. 2017" "no information at all"
8.4.7 Rozdělení řetězců podle vzoru
Často je potřeba rozdělit řetězec do několika částí oddělených nějakým vzorem. K tomu slouží funkce str_split(s, p, n)
a str_split_fixed(s, p, n)
, které rozdělí řetězec s
v bodech, kde je nalezen vzor p
. Celé číslo n
určuje, na kolik částí je řetězec rozdělen. Funkce str_split()
nepotřebuje mít n
zadané (implicitně rozdělí řetězec do tolika částí, do kolika je potřeba). Funkce vrací seznam, jehož každý prvek odpovídá jednomu prvku vektoru s
. Funkce str_split_fixed()
musí mít zadaný počet n
a vrací matici, jejíž řádky odpovídají prvkům vektoru s
a sloupce jednotlivým nálezům (přebytečné sloupce jsou naplněné prázdným řetězcem ""
).
ovoce <- c("jablka a hrušky a hrozny", "pomeranče a fíky a datle a pomela")
str_split(ovoce, " a ")
## [[1]]
## [1] "jablka" "hrušky" "hrozny"
##
## [[2]]
## [1] "pomeranče" "fíky" "datle" "pomela"
str_split(ovoce, " a ", 3)
## [[1]]
## [1] "jablka" "hrušky" "hrozny"
##
## [[2]]
## [1] "pomeranče" "fíky" "datle a pomela"
str_split_fixed(ovoce, " a ", 4)
## [,1] [,2] [,3] [,4]
## [1,] "jablka" "hrušky" "hrozny" ""
## [2,] "pomeranče" "fíky" "datle" "pomela"
str_split_fixed(ovoce, " a ", 3)
## [,1] [,2] [,3]
## [1,] "jablka" "hrušky" "hrozny"
## [2,] "pomeranče" "fíky" "datle a pomela"
Pokud je počet n
nedostatečný, je nerozdělený zbytek řetězce vložen do posledního zadaného sloupce.
Příklad: rozdělit dobře formátované datum v české konvenci na den, měsíc a rok:
datum <- c("1.6.2001", "1. 2. 2003", "blábol")
str_split_fixed(datum, "\\.\\s*", 3)
## [,1] [,2] [,3]
## [1,] "1" "6" "2001"
## [2,] "1" "2" "2003"
## [3,] "blábol" "" ""
Příklad: rozdělit datum ve tvaru “DD.MM.YYYY” nebo “DD. MM. YYYY” nebo “DD-MM-YYYY” na den, měsíc a rok:
datum <- c("1-5-1999", "1.6.2001", "1. 2. 2003", "blábol")
str_split_fixed(datum, "(\\.\\s*|-)", 3)
## [,1] [,2] [,3]
## [1,] "1" "5" "1999"
## [2,] "1" "6" "2001"
## [3,] "1" "2" "2003"
## [4,] "blábol" "" ""
8.4.8 Extrakce slov
Funkce word()
rozdělí řetězec na slova a vrátí slova se zadanými indexy. Použití je
word(s, start, end, sep)
kde s
je vektor rozdělovaných řetězců, start
je index prvního vraceného slova, end
je index posledního vraceného slova a sep
je regulární výraz, který odděluje slova (implicitně je to fixed(" ")
, tj. jedna mezera). Indexy mohou být i záporné – pak se počítají odzadu, tj. -1
je poslední slovo. Všechny parametry se recyklují.
Na příklady se podívejte do dokumentace.
8.4.9 Modifikace chování regulárních výrazů
Chování regulárních výrazů je možné ve funkcích z balíku stringr modifikovat pomocí čtyř funkcí:
fixed(pattern, ignore_case = FALSE)
porovnává řetězec doslovně, takže znaky bere doslovně jako byty. Je to rychlé, ale nemusí to vždy fungovat pro ne-ASCII znaky. Pokud se má např. tečka chápat doslovně jako tečka a ne jako jakýkoli znak, stačí ji zabalit:fixed(".")
.coll(pattern, ignore_case = FALSE, locale = NULL, ...)
porovnává řetězce tak, že respektuje standard collation rules.regex(pattern, ignore_case = FALSE, multiline = FALSE, comments = FALSE, dotall = FALSE, ...)
je použit implicitně. Používá regulární výrazy podle standardu ICU a umožňuje je mírně modifikovat.boundary(type = c("character", "line_break", "sentence", "word"), skip_word_none = TRUE, ...)
hledá hranice mezi věcmi. Např.boundary("word")
hledá hranice mezi slovy.
Argumenty funkcí mají následující významy:
pattern
je vzor (regulární výraz), jehož chování chceme modifikovatignore_case
zda se má ignorovat rozdíl mezi malými a velkými písmeny (TRUE
chápe malá a velká písmena jako zaměnitelná)locale
určí locale, které se má použít pro porovnávání, vizstri_locale_list()
; implicitně systémové localemultiline
– pokud jeTRUE
, pak$
a^
matchují začátek a konec řádku; pokud jeFALSE
(což je implicitní hodnota), pak mačují začátek a konec celého řetězcecomments
– pokud jeTRUE
, pak se ignorují mezerové znaky a komentáře začínající#
; doslovné mezery je třeba escapovat pomocí zpětného lomítkadotall
– pokud jeTRUE
, tečka znamená nejen znaky, ale i konec řádkutype
označí typ hranice, která se hledá; může nabývat hodnot"character"
,"line_break"
,"sentence"
,"word"
skip_word_none
ignoruje “slova”, která neobsahují žádná písmena nebo číslice, tj. punctuation.
Příklady z dokumentace:
pattern <- "a.b"
strings <- c("abb", "a.b")
str_detect(strings, pattern)
## [1] TRUE TRUE
str_detect(strings, fixed(pattern))
## [1] FALSE TRUE
str_detect(strings, coll(pattern))
## [1] FALSE TRUE
# coll() is useful for locale-aware case-insensitive matching
i <- c("I", "\u0130", "i")
i
## [1] "I" "İ" "i"
str_detect(i, fixed("i", TRUE))
## [1] TRUE FALSE TRUE
str_detect(i, coll("i", TRUE))
## [1] TRUE FALSE TRUE
str_detect(i, coll("i", TRUE, locale = "tr"))
## [1] FALSE TRUE TRUE
# Word boundaries
words <- c("These are some words.")
str_count(words, boundary("word"))
## [1] 4
str_split(words, " ")[[1]]
## [1] "These" "are" "" "" "some" "words."
str_split(words, boundary("word"))[[1]]
## [1] "These" "are" "some" "words"
# Regular expression variations
str_extract_all("The Cat in the Hat", "[a-z]+")
## [[1]]
## [1] "he" "at" "in" "the" "at"
str_extract_all("The Cat in the Hat", regex("[a-z]+", TRUE))
## [[1]]
## [1] "The" "Cat" "in" "the" "Hat"
str_extract_all("a\nb\nc", "^.")
## [[1]]
## [1] "a"
str_extract_all("a\nb\nc", regex("^.", multiline = TRUE))
## [[1]]
## [1] "a" "b" "c"
str_extract_all("a\nb\nc", "a.")
## [[1]]
## character(0)
str_extract_all("a\nb\nc", regex("a.", dotall = TRUE))
## [[1]]
## [1] "a\n"
8.4.10 Funkce, které vrací seznamy
Mnoho funkcí z balíku stringr vrací seznamy. Oddíl “Functions that return lists” ve vinětě k balíku stringr ukazuje, jak dál jak dál zpracovávat výstupy těchto funkcí.