/find me/.match 'Can you find me here?' # => #<MatchData "find me">
.
, \w
, \d
, +
, *
, ?
и още +/-3?
Wait, forgot to escape a space. Wheeeeee[taptaptap]eeeeee.
Имаме следната задача:
Да се напише кратък Ruby expression, който проверява дали дадено число е просто или не, посредством употреба на регулярен израз. Резултатът от изпълнението му трябва да еtrue
за прости числа иfalse
за всички останали. Неща, които можете да ползвате:
- Самото число, разбира се.
- Произволни методи от класа
Regexp
- Подходящ регулярен израз (шаблон)
- Текстовия низ
'1'
.String#*
.- Някакъв условен оператор (например
if
-else
или? … : …
)true
,false
, ...
Имаме следната задача:
Да валидирате изрази от следния тип за правилно отворени/затворени скоби:
(car (car (car ...)))
- Например:
(car (car (car (car list))))
- Целта е израз, чийто резултат да може да се ползва в условен оператор (
true
/false
-еквивалент)- Можете да ползвате произволни методи от класа
Regexp
- И регулярен израз, разбира се
Сменете * на % ако тя не е екранирана (escape-ната)
foo*
=>foo%
foo\*
=>foo\*
foo\\*
=>foo\\%
*\**
=>%\*%
- Може и да стане без look-ahead/behind/... :)
Проверете дали нещо е валиден математически израз
- Произволно число цяло
1337
- Променлива (латинска малка буква)
x
- Операция между валидни изрази (+, -, *, /)
x + y - 21 / 3
- Скоби, ограждащи валидни изрази
-x * (y + -5 * (7 - 13)) / 44 - 9000
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
will liquify the nerves of the sentient whilst you observe, your psyche withering in the onslaught of horror
grep
, sed
, awk
, vi
, Emacs
...
Regexp
/pattern/
%r
, например: %r{/path/maching/made/easy}
Regexp
String
също има методи за работа с регулярни изразиRegexp#match
nil
, ако шаблонът не "хваща" нищо
MatchData
, ако шаблонът "хваща" нещо от низа
MatchData
в детайли — по-късно
/wool/
ще отговаря на точно тази последователност от символи в низ
(
, )
, [
, ]
, {
, }
, .
, ?
, +
, *
, ^
, $
, \
, ...
-
)/find me/.match 'Can you find me here?' # => #<MatchData "find me">
/find me/.match 'You will not find ME!' # => nil
.
съвпада с един произволен символ (с изключение на символите за нов ред)
[
и ]
се ползват за дефиниране на класове от символи
*
, ?
, +
, {
и }
се ползват за указване на повторения
^
, $
, \b
, \B
и т.н. са "котви" и съответстват на определени "междусимволни дупки" :)
|
има смисъл на "или", например:/day|nice/.match 'A nice dance-day.' # => #<MatchData "nice">
/da(y|n)ce/.match 'A nice dance-day.' # => #<MatchData "dance" 1:"n">
Внимавайте с приоритета на |
\
пред специален символ го прави неспециален такъв (екранира го)
\
(като в обикновен низ)[
и ]
[a-z]
или [0-9A-F]
[a\-b]
[-abc]
или [abc-]
- тук то няма специален смисъл
^
, това означава "някой символ, който не е посочен в класа"
/W[aeiou]rd/.match "Word" # => #<MatchData "Word">
/[0-9a-f]/.match '9f' # => #<MatchData "9">
/[9f]/.match '9f' # => #<MatchData "9">
/[^a-z]/.match '9f' # => #<MatchData "9">
\w
- символ от дума ([a-zA-Z0-9_]
)
\W
- символ, който не може да участва в дума ([^a-zA-Z0-9_]
)
\d
- цифра ([0-9]
)
\D
- символ, който не е цифра ([^0-9]
)
\h
- шеснадесетична цифра ([0-9a-fA-F]
)
\H
- символ, който не е шеснадесетична цифра ([^0-9a-fA-F]
)
\s
- whitespace-символ (/[ \t\r\n\f]/
)
\S
- символ, който не е whitespace (/[^ \t\r\n\f]/
)[[:alpha:]]
- символ от азбука
[[:alnum:]]
- горното или цифра
[[:blank:]]
- интервал или таб
[[:cntrl:]]
- контролен символ
[[:digit:]]
- цифра
[[:lower:]]
- малка буква
[[:upper:]]
- главна буква
[[:print:]]
- printable-символ
[[:punct:]]
- пунктуационен символ
[[:space:]]
- whitespace-символ (вкл. и нов ред)
[[:xdigit:]]
- шеснадеситична цифра
[[:word:]]
- символ, който може да участва в дума (работи и за Unicode, за разлика от \w
)
[[:ascii:]]
- ASCII-символ\p{}
може да match-вате символи, имащи определено свойство (подобно на POSIX)
\p{Alnum}
, \p{Alpha}
, \p{Blank}
, \p{Cntrl}
, \p{Digit}
, \p{Graph}
\p{Katakana}
\p{Cyrillic}
, например:/\s\p{Cyrillic}\p{Cyrillic}\p{Cyrillic}/.match 'Ние сме на всеки километър!' # #<MatchData " сме">
^
съвпада с началото на ред (Ruby е в multiline режим по подразбиране)
$
съвпада с края на ред
\A
съвпада с началото на текстов низ
\z
съвпада с края на низ
\b
отговаря на граница на дума (когато е извън [
и ]
; вътре означава backspace
)
\B
отговаря на място, което не е граница на дума/real/.match "surrealist" # => #<MatchData "real">
/\Areal/.match "surrealist" # => nil
/\band/.match "Demand" # => nil
/\Band.+/.match "Supply and demand curve" # => #<MatchData "and curve">
s
s*
означава нула или повече повторения на s
s+
търси едно или повече повторения на s
s?
съвпада с нула или едно повторение на s
s{m,n}
означава между m и n повторения на s
m
или n
:
s{,n}
има смисъл на нула до n
повторения, а s{m,}
— поне m
повторения
s{n}
означава точно n
повторения/e+/.match 'Keeewl' # => #<MatchData "eee">
/[Kke]+/.match 'Keeewl' # => #<MatchData "Keee">
/\w+/.match '2038 - the year' # => #<MatchData "2038">
/".*"/.match '"Quoted text!"' # => #<MatchData "\"Quoted text!\"">
/[[:upper:]]+[[:lower:]]+l{2}o/.match 'Hello' # => #<MatchData "Hello">
?
след повторителя
.*?
кара повторителя *
да се държи не-лакомо
/<.+>/.match("<a><b>") # => #<MatchData "<a><b>">
/<.+?>/.match("<a><b>") # => #<MatchData "<a>">
Символите (
и )
се използват за логическо групиране на части от шаблона с цел:
day
или dance
: /\bda(y|nce)\b/
Текстът, който match-ва частта на шаблона, оградена в скоби, може да се достъпва:
за първата група,
за втората и т.н.
MatchData
-обекта
$1
, $2
... за номерирани групи
date_string = '2015-11-30'
date_parts = /\A(\d{4})-(\d\d)-(\d\d)\z/.match(date_string)
if date_parts
Date.new date_parts[1].to_i, date_parts[2].to_i, date_parts[3].to_i
# #<Date: 2015-11-30 ...>
end
(foo|bar)
, но не искаме тази група да се намесва в $1
, $2
, ...
(?:foo|bar)
/(\d+)(st|nd|rd|th)? (\w+)/.match '1st June' # => #<MatchData "1st June" 1:"1" 2:"st" 3:"June">
/(\d+)(?:st|nd|rd|th)? (\w+)/.match '1st June' # => #<MatchData "1st June" 1:"1" 2:"June">
/(\d+)(st|nd|rd|th)? (\w+)/.match '1 June' # => #<MatchData "1 June" 1:"1" 2:nil 3:"June">
/(\d+)(?:st|nd|rd|th)? (\w+)/.match '1 June' # => #<MatchData "1 June" 1:"1" 2:"June">
=~
и !~
— дефинирани в Regexp
и в String
/pattern/ =~ 'Some string'
'Some string' =~ /pattern/
nil
, ако няма съвпадение, или число (offset), ако има такова
$1
, $~
...)
if
log_entry = "[2011-07-22 15:42:12] - GET / HTTP/1.1 200 OK"
if log_entry =~ /\bHTTP\/1\.1 (\d+)/
request_status = $1.to_i # => 200
else
raise "Malformed log entry!"
end
date_string = '2015-11-30'
if date_string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/
Date.new $1.to_i, $2.to_i, $3.to_i # #<Date: 2015-11-30 ...>
end
(?<name>)
или така: (?'name')
, където name
е името на групата
(?<date>\d{4}-\d{2}-\d{2})
/(?<date>\d{4}-\d{2}-\d{2})/.match 'Today is 2011-11-08, Tuesday.' # => #<MatchData "2011-11-08" date:"2011-11-08">
,
и прочее, ако групите ви не са именовани
1
се обръща към 11-тата група
, последван от символа 1
"?
\k<group_identifier>
, където group_identifier
е число или име на група
/(?<word>\w+), \k<word>/
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/.match 'Today is 2011-11-08, Tuesday.'
# #<MatchData "2011-11-08" year:"2011" month:"11" day:"08">
/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\11/.match 'Regular expressions'
# #<MatchData "ular express" 1:"u" 2:"l" 3:"a" 4:"r" 5:" " 6:"e" 7:"x" 8:"p" 9:"r" 10:"e" 11:"s">
/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\k<11>1/.match 'Regular express1ions'
# #<MatchData "ular express1" 1:"u" 2:"l" 3:"a" 4:"r" 5:" " 6:"e" 7:"x" 8:"p" 9:"r" 10:"e" 11:"s">
/(\w+), \1/.match 'testing, testing' # => #<MatchData "testing, testing" 1:"testing">
/(\w+), \1/.match 'testing, twice' # => nil
/(?<word>\w+), \k<word>/.match 'testing, testing' # => #<MatchData "testing, testing" word:"testing">
/".*"/.match '"Quoted"' # => #<MatchData "\"Quoted\"">
Частта от шаблона .*
хваща Quoted"
, тъй като е алчна. Това води до невъзможност да се намери съвпадение и алгоритъмът backtrack-ва -- връща се една стъпка/символ назад.
(?>pattern)
/"(?>.*)"/.match('"Quote"') # => nil
\g<name>
, където name
е номер или име на група в шаблона
/(\w+), \1/.match 'testing, twice' # => nil
/(\w+), \g<1>/.match 'testing, twice' # => #<MatchData "testing, twice" 1:"twice">
Да валидирате изрази от следния тип за правилно отворени/затворени скоби:
(car (car (car ...)))
- Например:
(car (car (car (car list))))
- Целта е израз, чийто резултат да може да се ползва в условен оператор (
true
/false
-еквивалент)- Можете да ползвате произволни методи от класа
Regexp
- И регулярен израз, разбира се
validator = /^(\(car (\g<1>*|\w*)\))$/
valid = '(car (car (car (car list))))'
invalid = '(car (car (car list))'
validator.match(valid) ? true : false # => true
validator.match(invalid) ? true : false # => false
Проверете дали нещо е валиден математически израз
- Произволно цяло число
1337
- Променлива (малка латинска буква)
x
- Знак пред валиден израз (+, -)
-33 + 22 * -y
- Операция между валидни изрази (+, -, *, /)
x + y - 21 / 3
- Скоби, ограждащи валидни изрази
-x * (y + -5 * (7 - 13)) / 44 - 9000
validator = /^([-+]?(\d+|[a-z]|\(\g<1>\)|\g<1> [-+*\/] \g<1>))$/
valid = '-(3 + (x * (7 / y))) * (44 * y - z / 22)'
invalid = '((33 - 7) * x'
validator.match(valid) ? true : false
validator.match(invalid) ? true : false
/^([-+]?(\d+|[a-z]|\(\g<1>\)|\g<1> [-+*\/] \g<1>))$/# ~> -:1: never ending recursion: /^([-+]?(\d+|[a-z]|\(\g<1>\)|\g<1> [-+*\/] \g<1>))$/
validator = /^([-+]?(\d+|[a-z]|\(\g<1>\))( [-+*\/] \g<1>)?)$/
valid = '-(3 + (x * (7 / y))) * (44 * y - z / 22)'
invalid = '((33 - 7) * x'
validator.match(valid) ? true : false # => true
validator.match(invalid) ? true : false # => false
/(?=pattern)/
/(?!pattern)/
/(?<=pattern)/
/(?<!pattern)/
/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>") # => #<MatchData "bold">
Сменете * на % ако тя не е екранирана (escape-ната)
foo*
=>foo%
foo\*
=>foo\*
foo\\*
=>foo\\%
*\\**
=>%\\*%
"*\\**".gsub(/((?<!\\)(?:\\\\)*)\*/, '\1%') # => "%\\*%"
\
, които нямат пред себе си \
*
и готово :)"*\\**".gsub(/\G([^*\\]*(?:\\.[^*\\]*)*)\*/, '\1%') # => "%\\*%"
Между другото, регулярните изрази поддържат интерполация:
name = /[^@]+/
host = /\w+\.(com|net|org)/
email = /#{name}@#{host}/ # => /(?-mix:[^@]+)@(?-mix:\w+\.(com|net|org))/
Regexp#match
$~
Regexp.last_match
Enumerable
MatchData#[група]
, където група
е номер или име на група, ви връща порцията текст, отговаряща на съответната група
MatchData#begin(група)
пък ви връща число — отместването спрямо началото на низа на порцията текст, отговаряща на съответната група
/(\w+)/.match('Some words')[1] # => "Some"
/(\w+)/.match('Some words').begin(1) # => 0
/(?<id>\d+)/.match('ID: 12345')[:id] # => "12345"
/(?<id>\d+)/.match('ID: 12345').begin(:id) # => 4
MatchData#pre_match
(същото като специалната променлива $`
) — текстът преди съвпадението
MatchData#post_match
(същото като специалната променлива $'
) — текстът след съвпадението
match = /(?<number>\d+)/.match 'ID: 12345 (new)'
match[:number] # => "12345"
match.pre_match # => "ID: "
match.post_match # => " (new)"
$~
, $'
, $1
, $2
, $3
и прочее
match
Regexp#match
html = '<h1>Header</h1>' # или:
html = '<img src="http://my/image.src" alt="Kartman Makes Burgers" />'
case html
when /(<h(\d)>)(.+)<\/h\2>/
{header: $3, size: $2}
when /<a\s+href="([^"]+)">([^<]+)<\/a>/
{url: $1, text: $2}
when /<img\s+src="([^"]+)"\s+alt="([^"]+)"\s*\/>/
{image: $1, alt: $2}
else
'unrecognized tag'
end
# {:image=>"http://my/image.src", :alt=>"Kartman Makes Burgers"}
String#match
String#=~
и String#!~
String#sub
, String#gsub
и вариантите им с !
String#[]
и String#slice
- в някои от вариантите си приемат регулярен израз
String#index
и rindex
приемат и регулярен израз
String#partition
и rpartition
и други...'SomeTitleCase'.gsub /(^|[[:lower:]])([[:upper:]])/ do
[$1, $2.downcase].reject(&:empty?).join('_')
end
# "some_title_case"
Цитат от документацията:
A regexp can be matched against a string when they either share an encoding, or the regexp’s encoding is US-ASCII and the string’s encoding is ASCII-compatible.
Regexp#encoding
/something/u
за UTF-8
Rubyのお父さんはまつもとゆきひろさんです。
unicode_test = 'Rubyのお父さんはまつもとゆきひろさんです。'
/は[[:alpha:]]+さん/.match unicode_test # #<MatchData "はまつもとゆきひろさん">
\b
в Unicode-текст работи, когато границата на думата е лесно определима
/\b[[:alpha:]]\b/.match 'това и онова' # #<MatchData "и">
Rubyのお父さんはまつもとゆきひろさんです。
?Например:
'Ruby no otousan ha Matsumoto Yukihiro san desu.'.gsub(/(\b[[:alpha:]]+\b)/) { "[#{$1}]" }
# "[Ruby] [no] [otousan] [ha] [Matsumoto] [Yukihiro] [san] [desu]."
Но:
'Rubyのお父さんはまつもとゆきひろさんです。'.gsub(/(\b[[:alpha:]]+\b)/) { "[#{$1}]" }
# "[Rubyのお父さんはまつもとゆきひろさんです]。"
/pattern/flags
i
прави търсенето на съвпадение нечувствително към регистъра на буквите
u
кара шаблона да носи задължителна кодировка UTF-8
m
превръща шаблона в multiline-такъв (в този режим, например, .
ще съвпада и с нов ред)
(?(cond)yes|no)
cond
може да е референция към друга група в шаблона, или пък look-ahead/behind
regexp = /^(number)?\s*(?(1)\d+|[a-zA-Z]+)$/
regexp =~ "number 123" # => 0
regexp =~ "foo" # => 0
regexp =~ "number baz" # => nil
Regexp
: http://www.ruby-doc.org/core-2.2.3/Regexp.html
MatchData
: http://www.ruby-doc.org/core-2.2.3/MatchData.html
String
: http://www.ruby-doc.org/core-2.2.3/String.html
ri
, например: ri Regexp#=~
Да се напише кратък Ruby expression, който проверява дали дадено число е просто или не, посредством употреба на регулярен израз. Резултатът от изпълнението му трябва да еtrue
за прости числа иfalse
за всички останали. Неща, които можете да ползвате:
- Самото число, разбира се.
- Произволни методи от класа
Regexp
- Подходящ регулярен израз (шаблон)
- Текстовия низ
'1'
.String#*
.- Някакъв условен оператор (например
if
-else
или? … : …
)true
,false
, ...
'1' * числото =~ /някакъв регулярен израз/ ? false : true
'1' * 13 =~ /^1?$|^(11+?)+$/ ? false : true
Like any...