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?
regexp
regexp.Regexp
Find(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)\b
re := 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,}?