Em Ruby podemos usar o alguns métodos na classe String
juntamente com expressões regulares (Regex) como match
ou scan
. Mas muitas vezes queremos recuperar vários dados dessa string e aí se torna uma confusão, principalmente na ordem em que as capturas aconteceram.
Para resolver esse problema, a gente pode usar a funcionalidade de capturas nominadas.
Como exemplo vamos usar um código ISBN-13 bastante utilizado no mundo. Neste código os três primeiros dígitos são o EAN, seguido de dois dígitos que representam o grupo, quatro dígitos da editora, três dígitos do número do livro e um dígito verificador.
Se fossemos fazer um regex (focado na legibilidade) para o livro abaixo, ficaria algo mais ou menos assim:
isbn = '9780134456478'
book_isbn = isbn.match(/(\d{3})(\d{2})(\d{4})(\d{3})(\d{1})/)
# => #<MatchData "9780134456478" 1:"978" 2:"01" 3:"3445" 4:"647" 5:"8">
book_isbn.captures
# => ["978", "01", "3445", "647", "8"]
Para salvar em variáveis poderíamos fazer algo assim:
isbn = '9780134456478'
ean, group, publisher, title, check = isbn.match(/(\d{3})(\d{2})(\d{4})(\d{3})(\d{1})/)&.captures
# => ["978", "01", "3445", "647", "8"]
Assim, temos os valores salvos em variáveis para utilizar. Até aí já está bem interessante, mas algumas vezes alocar variáveis demais ou o tratamento de capturas que não aconteceram podem se tornar um problema.
Então podemos trabalhar com capturas nominadas para facilitar atribuindo menos variáveis e dando mais valor à captura. Para isso, colocamos uma sintaxe antes do grupo da seguinte forma: (?<nome_da_captura>match_aqui)
.
Seguindo o exemplo acima ficaria assim:
isbn = '9780134456478'
book_isbn =
isbn.match(/(?<ean>\d{3})(?<group>\d{2})(?<publisher>\d{4})(?<title>\d{3})(?<check>\d{1})/)
# => #<MatchData "9780134456478" ean:"978" group:"01" publisher:"3445" title:"647" check:"8">
book_isbn[:ean]
# => "978"
book_isbn[:group]
# => "01"
book_isbn[:publisher]
# => "3445"
book_isbn[:title]
# => "647"
book_isbn[:check]
# => "8"
Agora você tem o melhor dos dois mundos, um regex fácil de ler e uma sintaxe de hash disponível na MatchData
que você pode utilizar no seu código.
Meu uso preferido seria algo assim:
ISBN_REGEX = /^(?<ean>\d{3})(?<group>\d{2})(?<publisher>\d{4})(?<title>\d{3})(?<check>\d{1})$/.freeze
book_isbn = ISBN_REGEX.match('9780134456478')
# => #<MatchData "9780134456478" ean:"978" group:"01" publisher:"3445" title:"647" check:"8">
Você pode testar o regex do exemplo aqui no Rubular que, diga-se de passagem, é um excelente lugar para testar regex com Ruby.
Por hoje é só, bom código!