
[ad_1]
Processar strings para extrair, manipular ou pesquisar dados é uma habilidade essencial que a maioria dos engenheiros de software precisa adquirir. Expressões regulares, ou regex, são o venerável titã do processamento de strings. Expressões regulares são parte integrante de muitas ferramentas de linha de comando encontradas em Unix, mecanismos de busca, processadores de texto e editores de texto. A desvantagem das expressões regulares é que elas podem ser difíceis de criar, ler e depurar. A maioria das linguagens de programação suporta alguma forma de regex – e agora o Swift também suporta o Swift Regex.
Um novo e empolgante Regex Builder no Swift Regex nos oferece uma maneira programática de criar expressões regulares. Essa abordagem inovadora para criar expressões regulares muitas vezes complexas certamente será um vencedor instantâneo tanto para o neófito quanto para os aficionados em regex. Vamos explorar o Regex Builder para descobrir seus recursos de amplo alcance.
O Swift Regex traz suporte de primeira classe para expressões regulares para a linguagem Swift e visa mitigar ou eliminar completamente muitas das desvantagens do regex. O compilador Swift suporta nativamente a sintaxe regex, o que nos dá erros de tempo de compilação, realce de sintaxe e capturas fortemente tipadas. A sintaxe Regex no Swift é compatível com Perl, Python, Ruby, Java, NSRegularExpression e muitos outros.
Deve-se notar que, no momento da redação deste artigo, o Swift Regex ainda está no período beta aberto. Usaremos o Swift Regex encontrado no Xcode 14 beta 6.
Criando uma expressão regular Swift
O Swift Regex oferece suporte à criação de uma expressão regular de várias maneiras diferentes, cada uma útil para diferentes cenários. Primeiro, vamos dar uma olhada na criação de uma expressão regular em tempo de compilação.
Compile Time Regex
let regex = /\d/
This regular expression will match a single digit. As is typical in regular expression syntax, the expression can be found between two forward slashes; “/<expression>/”. As you can see, this regular expression is a first-class type in Swift and can be assigned directly to a variable. As a Swift type, Xcode will also recognize this regex and provide both compile time checks and syntax highlighting.
Swift has added robust support for regex to a number of common APIs, and using this regular expression couldn’t be easier.
let user = "{name: Shane, id: 123, employee_id: 456}" let regex = /name: \w+/ if let match = user.firstMatch(of: regex) { print(match.output) }
Which gives us the output:
name: Shane
Você pode ficar tentado a usar a expressão regular [a-zA-Z]+
para combinar uma palavra aqui. No entanto, usando \w+
permite que o sistema leve em consideração a localidade atual.
Regex de tempo de execução
O Swift Regex também suporta a criação de expressões regulares em tempo de execução. A criação em tempo de execução de uma expressão regular tem muitos usos e pode ser útil para editores, ferramentas de linha de comando e pesquisa apenas para citar alguns. A sintaxe da expressão é a mesma de uma expressão de tempo de compilação. No entanto, eles são criados de uma maneira ligeiramente diferente.
let regex = try Regex(".*\(searchTerm).*")
This regular expression is looking for a specific search term supplied at runtime. Here the regular expression is created by constructing the Regex
type with a String
representing the regular expression. The try
keyword is used since a Regex
can throw an error if the supplied regular expression is invalid.
We can again apply this regex using the firstMatch(of:)
function as in our first example. Note that this time our regex captures the line that matches by using a regex capture, (
, and )
.
let users = """ [ {name: Shane, id: 123, employee_id: 456}, {name: Sally, id: 789, employee_id: 101}, {name: Sam, id: 453, employee_id: 999} ] """ let idToSearch = 789 let regex = try Regex("(.*id: \(idToSearch).*)") if let match = users.firstMatch(of: regex) { print(match.output[1].substring ?? "not found") }
Running the example gives us the following output:
{name: Sally, id: 789, employee_id: 101},
Podemos obter acesso a quaisquer dados capturados pelo regex via output
no retorno Regex.Match
estrutura. Aqui, output
é um existencial com o primeiro item, no índice 0
, sendo os dados de entrada regex. Cada captura definida na regex é encontrada em índices subsequentes.
Construtor de Regex
O inovador e novo Regex Builder apresenta uma abordagem declarativa para compor expressões regulares. Essa nova maneira incrível de criar expressões regulares abrirá a porta da regex para qualquer pessoa que ache difícil de entender, manter ou criar. O construtor Regex é a solução do Swift para as desvantagens da sintaxe de expressão regular. O construtor Regex é um DSL para criar expressões regulares com segurança de tipo, enquanto ainda permite facilidade de uso e expressividade. Basta importar o novo RegexBuilder
módulo, e você terá tudo o que precisa para criar e compor expressões regulares poderosas.
import RegexBuilder let regex = Regex { One(.digit) }
This regular expression will match a single digit and is functionally equivalent to our first compile time regex example, /\d/
. Here the standard regex syntax is discarded in favor of a declarative approach. All regex operations, including captures, can be represented with RegexBuilder
. In addition, when it makes sense, regex literals can be utilized right within the regex builder. This makes for a very expressive and powerful approach to creating regular expressions.
RegexBuilder Example
Let’s take a deeper look into RegexBuilder
. In this example, we will use a regex builder to parse and extract information from a Unix top
command.
top -l 1 -o mem -n 8 -stats pid,command,pstate,mem | sed 1,12d
For simplicity, we’ll take the output of running this command and assign it to a Swift variable.
// PID COMMAND STATE MEMORY let top = """ 45360 lldb-rpc-server sleeping 1719M 2098 Google Chrome sleeping 1679M- 179 WindowServer sleeping 1406M 106 BDLDaemon running 1194M 45346 Xcode running 878M 0 kernel_task running 741M 2318 Dropbox sleeping 4760K+ 2028 BBEdit sleeping 94M """
As you can see, the top
command outputs structured data that is well suited for use with regular expressions. In our example, we will be extracting the name, status, and size of each item. When considering a Regex Builder it is useful to break a larger regex down into smaller component parts which are then concatenated by the builder. First, I’ll present the code, and then we’ll discuss how it works.
// 1 let separator = /\s{1,}/ // 2 let topMatcher = Regex { // 3 OneOrMore(.digit) // 4 separator // 5 Capture( OneOrMore(.any) ) separator // 6 Capture( ChoiceOf { "running" "sleeping" "stuck" "idle" "stopped" "halted" "zombie" "unknown" } ) separator // 7 Capture { OneOrMore(.digit) // /M|K|B/ ChoiceOf { "M" "K" "B" } Optionally(/\+|-/) } } // 8 let matches = top.matches(of: topMatcher) for match in matches { // 9 let (_, name, status, size) = match.output print("\(name) \t\t \(status) \t\t \(size)") }
Running the example gives us the following output:
lldb-rpc-server sleeping 1719M Google Chrome sleeping 1679M- WindowServer sleeping 1406M BDLDaemon running 1194M Xcode running 878M kernel_task running 741M Dropbox sleeping 4760K+ BBEdit sleeping 94M
Aqui está um detalhamento do que está acontecendo com o código:
- Observando os dados, podemos ver que cada coluna é separada por um ou mais espaços. Aqui definimos uma regex em tempo de compilação e a atribuímos ao
separator
variável. Podemos então usarseparator
dentro do construtor regex para corresponder aos separadores de coluna. - Defina o construtor regex como um fechamento à direita para
Regex
e atribua-otopMatcher
. - Um quantificador que corresponde a uma ou mais ocorrências do especificado
CharacterClass
.CharacterClass
é uma estrutura que está em conformidade comRegexComponent
e é semelhante em função de umCharacterSet
. o.digit
CharacterClass define um dígito numérico. - Corresponde ao separador de coluna.
- Captura um ou mais de qualquer personagem. As capturas de Regex são retornadas no
Output
da regex e são indexados com base em sua posição dentro da regex. - Uma captura de um item da lista de itens incluída.
ChoiceOf
é equivalente a uma alternância regex (o|
operador regex) e não pode ter um bloco vazio. Você pode pensar nisso como correspondendo a um único valor de umEnum
. Use quando houver uma lista conhecida de valores a serem correspondidos pela expressão regular. - Captura um ou mais dígitos seguidos por um item da lista conhecida de “M”, “K” ou “B” opcionalmente seguido por um “+” ou “-“. Observe que o
Optionally
componente pode ter um literal regex como seu parâmetro. - Aqui passamos nossa regex como parâmetro para o
matches(of:)
função. Atribuímos o valor retornado a uma variável que permitirá acessar a saída regex e nossos dados capturados. - o
output
A propriedade dos dados retornados pela regex contém todos os dados de entrada seguidos por quaisquer dados capturados. Aqui estamos desempacotando ooutput
tupla ignorando o primeiro item (a entrada) e atribuindo cada item subsequente a uma variável para facilitar o acesso.
Como você pode ver neste exemplo, o construtor Swift regex é uma maneira poderosa e expressiva de criar expressões regulares em Swift. Esta é apenas uma amostra de sua capacidade. Então, a seguir, vamos dar uma olhada mais profunda no construtor Swift regex e suas capturas fortemente tipadas.
Capturas fortemente tipadas no Swift RegexBuilder
Um dos recursos mais exclusivos e atraentes do construtor Swift regex são as capturas fortemente tipadas. Em vez de simplesmente retornar uma correspondência de string, o Swift Regex pode retornar um tipo forte representando os dados capturados.
Em alguns casos, especialmente por motivos de desempenho, podemos querer sair mais cedo se uma captura de regex não atender a alguns critérios adicionais. TryCapture
nos permite fazer isso. o TryCapture
O componente Regex Builder passará um valor capturado para um transform
encerramento onde podemos realizar validação adicional ou transformação de valor. Quando o transform
O fechamento retorna um valor, seja a versão original ou modificada, é considerado válido e o valor é capturado. No entanto, quando o transform
retornos de fechamento nil
a correspondência é sinalizada como falha e fará com que o mecanismo regex retroceda e tente um caminho alternativo. TryCapture
s transform
encerramento participa ativamente no processo de correspondência. Este é um recurso poderoso e permite uma correspondência extremamente flexível.
Vamos dar uma olhada em um exemplo.
Neste exemplo, usaremos um construtor regex para analisar e extrair informações de um Unix syslog
comando.
syslog -F '$((Time)(ISO8601)) | $((Level)(str)) | $(Sender)[$(PID)] | $Message'
Pegaremos o resultado da execução deste comando e o atribuiremos a uma variável Swift.
// TIME LEVEL PROCESS(PID) MESSSAGE let syslog = """ 2022-06-09T14:11:52-05 | Notice | Installer Progress[1211] | Ordering windows out 2022-06-09T14:12:18-05 | Notice | Installer Progress[1211] | Unable to quit because there are connected processes 2022-06-09T14:12:30-05 | Critical | Installer Progress[1211] | Process 648 unexpectedly went away 2022-06-09T14:15:31-05 | Alert | syslogd[126] | ASL Sender Statistics 2022-06-09T14:16:43-05 | Error | MobileDeviceUpdater[3978] | tid:231b - Mux ID not found in mapping dictionary """
Next, we use Swift Regex to extract this data, including the timestamp, a strongly typed severity level, and filtering of processes with an id of less than 1000.
let separator = " | " let regex = Regex { // 1 Capture(.iso8601(assuming: .current, dateSeparator: .dash)) // 2 "-" OneOrMore(.digit) separator // 3 TryCapture { ChoiceOf { "Debug" "Informational" "Notice" "Warning" "Error" "Critical" "Alert" "Emergency" } } transform: { // 4 SeverityLevel(rawValue: String($0)) } separator // 5 OneOrMore(.any, .reluctant) "[" Capture { OneOrMore(.digit) } transform: { substring -> Int? in // 6 let pid = Int(String(substring)) if let pid, pid >= 1000 { return pid } return nil } "]" separator OneOrMore(.any) } // 7 let matches = syslog.matches(of: regex) print(type(of: matches[0].output)) for match in matches { let (_, date, status, pid) = match.output // 8 if let pid { print("\(date) \(status) \(pid)") } } // 9 enum SeverityLevel: String { case debug = "Debug" case info = "Informational" case notice = "Notice" case warning = "Warning" case error = "Error" case critical = "Critical" case alert = "Alert" case emergency = "Emergency" }
A execução do exemplo nos dá a seguinte saída:
(Substring, Date, SeverityLevel, Optional<Int>) 2022-06-09 19:11:52 +0000 notice 1211 2022-06-09 19:12:18 +0000 notice 1211 2022-06-09 19:12:30 +0000 critical 1211 2022-06-09 19:16:43 +0000 error 3978
Aqui está o que está acontecendo com o syslog
exemplo.
- Aqui, estamos capturando uma data formatada ISO 8601. o
iso8601
função estática (nova no iOS 16) é chamada noDate.ISO8601FormatStyle
modelo. Esta função constrói e retorna um formatador de data para uso pelo Swift RegexCapture
na conversão da string capturada em umDate
. esteDate
é então usado naCapture
s sem necessidade de conversão de string até a data. - Após a data formatada em ISO 8601, temos um componente de fuso horário de deslocamento UTC correspondente ao traço e um ou mais dígitos.
- Aqui
TryCapture
está sendo usado para transformar um tipo de captura. Ele converterá o valor correspondente em um tipo não opcional ou falhará na correspondência. - o
transform
encerramento será chamado ao combinar a captura. É passado o valor de substring correspondente que pode então se transformar no tipo desejado. Neste exemplo, a transformação está convertendo a substring correspondente em umSeverityLevel
enum. A saída regex correspondente para esta captura torna-se o tipo de retorno de closures. No caso de uma transformação emTryCapture
este tipo não será opcional. ParaCapture
transform, o tipo será opcional. - Swift Regex define várias repetições, que são
OneOrMore
,ZeroOrMore
,Optionally
eRepeat
. o.reluctant
o comportamento de repetição corresponderá ao menor número de ocorrências possível. O comportamento de repetição padrão para todas as repetições é.eager
. - Uma captura de transformação transformará a substring correspondente de dígitos em um opcional
Int
valor. Se esse valor for 1000 ou maior, ele será retornado da transformação e se tornará o valor de saída de capturas. Caso contrário, ele retornanil
para isso captura a saída. - Atribua as correspondências da regex ao
matches
variável. - Se o
pid
captura não énil
depois imprima os dados. - Define o
SeverityLevel
tipo enum, que é usado pela captura de transformação definida em #3.
Conclusão
Swift Regex é uma adição bem-vinda e poderosa ao Swift. O Regex Builder é uma solução obrigatória para todas as necessidades de regex, exceto as mais simples, e dominá-lo será um tempo bem gasto. A abordagem declarativa do Regex Builder, juntamente com o suporte a regex em tempo de compilação, fornecendo erros de tempo de compilação, realce de sintaxe e capturas fortemente tipadas, cria uma combinação potente. Muito pensamento foi pensado no design do Swift Regex, e isso mostra. O Swift Regex será uma adição valiosa à sua caixa de ferramentas de desenvolvimento, e dedicar um tempo para aprendê-lo renderá dividendos.
Recursos
- Conheça o Swift Regex – WWDC 2022
- Swift Regex: Além do básico – WWDC 2022
- Proposta Swift Evolution 0351 – Regex builder DSL
- Proposta Swift Evolution 0354 – Literais Regex
- Proposta Swift Evolution 0355 – Sintaxe Regex e Construção em Tempo de Execução
- Proposta Swift Evolution 0357 – Algoritmos de processamento de string com tecnologia Regex
- Construtor de DSL Swift Regex
[ad_2]
Source link