pattern matching през 1962г.ed, sed, grep, egrep, awk, and lex...Най-общо: работа с текстови низове
Искаме да проверим дали даден низ е валиден мобилен номер.
Това означава:
func isValidMobilePhone(number string) bool {
code := number[:3]
if code != "087" && code != "088" && code != "089" {
return false
}
if number[3] == '0' || number[3] == '1' {
return false
}
return len(number[4:]) == 6
}func isValidMobilePhone(number string) bool {
return regexp.MustCompile(`^08[789][2-9]\d{6}$`).MatchString(number)
}Nice, a?
regexpregexp.RegexpFind(All)?(String)?(Submatch)?(Index)?godoc regexp godoc regexp/syntax
Всеки символ, освен някои специални, означава себе си.
Цялата магия е в специалните символи:
. \| ( ) [ ] { } + \ ^ $ * ?
Някои символи са специални само в определен контекст (например символът -)
Compile() или MustCompile()re, err := regexp.Compile(`Hello`) re := regexp.MustCompile(`Hello`)
Второто изпада в паника, ако регулярният израз не е валиден
package main
import (
"fmt"
"regexp"
)
func main() { re := regexp.MustCompile(`Hello`) if re.MatchString("Hello Regular Expression.") { fmt.Println("Match") } else { fmt.Println("No match") } }
package main
import (
"fmt"
"regexp"
)
// second_start OMIT
func matcher(pattern, text string) {
match := regexp.MustCompile(pattern).FindStringIndex(text)
if match == nil {
fmt.Printf("No match for `%s` in \"%s\"\n", pattern, text)
return
}
begin := match[0]
end := match[1]
fmt.Printf("%s(%s)%s\n", text[:begin], text[begin:end], text[end:])
}
func main() {
matcher(`pat`, "Find a pattern.") // Find a (pat)tern. matcher(`#`, "What the ###?") // What the (#)##?
}
В последното можем да пропуснем m или n:
matcher(`test a*`, "the test aaaa bbbb") // the (test aaaa) bbbb
matcher(`test b*`, "the test aaaa bbbb") // the (test )aaaa bbbb
matcher(`test a?`, "the test aaaa bbbb") // the (test a)aaa bbbb
matcher(`test b?`, "the test aaaa bbbb") // the (test )aaaa bbbb
matcher(`test a+`, "the test aaaa bbbb") // the (test aaaa) bbbb
matcher(`test b+`, "the test aaaa bbbb") // no match
matcher(`test a{1,3}`, "the test aaaa bbbb") // the (test aaa)a bbbb? след квантора.*? кара повторителя * да се държи не-лакомоmatcher(`[hH]o+`, "Hoooooohohooo...") // (Hoooooo)hohooo... matcher(`[hH]o+?`, "Hoooooohohooo...") // (Ho)ooooohohooo...
matcher(`o+`, "Goooooooogle") // G(oooooooo)gle matcher(`[hH]o+`, "Hohohoho...") // (Ho)hohoho...
Хм. Не искахме точно това. По-скоро:
matcher(`([hH]o)+`, "Hohohoho...") // (Hohohoho)...
matcher(`([hH]o){2,3}`, "Hohohoho...") // (Hohoho)ho...
Символите ( и ) се използват за логическо групиране на части от шаблона с цел:
Повече за тях -- след малко
Символът | има смисъла на "или"
matcher(`day|nice`, "A nice dance-day.") // A (nice) dance-day. matcher(`da(y|n)ce`, "A nice dance-day.") // A nice (dance)-day.
NB! Единствено | се прилага не над непосредствените му
символи/класове, а на целия низ отляво/отдясно:
matcher(`ab|c|e`, "abcdef") // (ab)cdef matcher(`am|c|e`, "abcdef") // ab(c)def matcher(`a(m)|c|e`, "abcdef") // ab(c)def
Набор от символи, заграден от [ и ], например [aeoui].
Съвпадат с точно един от символите, описани в класа:
matcher(`[aeoui]`, "Google") // G(o)ogle
Отрицание на символен клас -- ^ в началото на класа:
matcher(`[^CBL][aeoui]`, "Cobol") // 'Co(bo)l'
Диапазон от символи -- - между два символа в символен клас:
matcher(`[0-9]{1,3}-[a-z]`, "Figure 42-b") // Figure (42-b)
matcher(`[^a-zA-Z-]`, "Figure-42-b") // Figure-(4)2-b[a-zA-Z0-9_])[^a-zA-Z0-9_])[0-9])[^0-9])Символите ( и ) се използват за логическо групиране на части от шаблона с цел:
\bda(y|nce)\bre := regexp.MustCompile(`.at`)
res := re.FindAllStringSubmatch("The cat sat on the mat.", -1)
fmt.Printf("%v", res)
// [[cat] [sat] [mat]]Можем да разменим местата на две думи
re := regexp.MustCompile("([a-z]+) ([a-z]+)")
re.ReplaceAllString("foo bar", "$2 $1")или да имаме по-сложна логикa, върху всяко от съвпаденията
re.ReplaceAllStringFunc("foo with bar", func(match string) string {
if len(match) > 3 {
return match + "!!!"
}
return match
})
// foo with!!! barили ...
re := regexp.MustCompile("a(x*)b")
fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "Щ"))
// -Щ-Щ-Обяснете последното :)
Често strings.Split() е крайно недостатъчен.
re := regexp.MustCompile("[0-9]")
re.Split("Помощ1не2ми3работи4space5клавиша!", -1)
// []string{"Помощ" "не" "ми" "работи" "space" "клавиша!"}Вторият аргумент указва максималната дължина на върнатия слайс.
re := regexp.MustCompile("[0-9]")
re.Split("Помощ1не2ми3работи4space5клавиша!", 3)
// []string{"Помощ" "не" "ми3работи4space5клавиша!"}matcher(`\p{Cyrillic}`, "Bаba") // B(a)ba* първото а е "нашенско"
regexp.Compile(`(?i)n`)
package main
import (
"fmt"
"regexp"
)
func matcher(pattern, text string) { match := regexp.MustCompile(pattern).FindStringIndex(text) if match == nil { fmt.Printf("No match for `%s` in \"%s\"\n", pattern, text) return } begin := match[0] end := match[1] fmt.Printf("%s(%s)%s\n", text[:begin], text[begin:end], text[end:]) } func main() { matcher(`pat`, "Find a pattern.") // Find a (pat)tern. matcher(`#`, "What the ###?") // What the (#)##? }
Encouraging regular expressions as a panacea for all text processing
problems is not only lazy and poor engineering, it also reinforces
their use by people who shouldn't be using them at all.
-- Rob Pike
commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing-and.html
but is slow in Java, Perl, PHP, Python, Ruby, ...
Russ Cox
rsc@swtch.com
January 2007
Помните ли повторителите в регулярните изрази? И при тях можем да изключваме алчността.
Променя държанието в това да се стреми да хване възможно най-малко.
s*?s+?s??s{n,m}?s{n,}?