R: R Expressões regulares
Última atualização: 2026-08-26
Na aula anterior, aprendemos os conceitos básicos da manipulação de strings, mas 80% dos cenários do mundo real exigem correspondência de padrões — não a correspondência de strings fixas, mas a correspondência de “strings que seguem um determinado padrão”. Esse é o poder das expressões regulares (regex).
Ao concluir esta lição, você será capaz de usar expressões regulares no R para: validar números de telefone, extrair endereços de e-mail, analisar URLs, analisar logs e realizar mineração de texto.
1. O que você vai aprender
- Noções básicas sobre expressões regulares (caracteres, quantificadores, âncoras, grupos)
- R Sintaxe de expressões regulares (estilo POSIX/Perl)
- funções de expressões regulares do stringr (str_match/str_extract/str_detect)
- Grupos de captura e referências retroativas
- Correspondência gananciosa versus não gananciosa
- Guia prático: verificação por meio de número de telefone, e-mail, URL e número de identificação
2. Uma história sobre análise de logs
(1) Desafio: O que fazer com um milhão de entradas de log
Alice é uma engenheira de operações que precisa extrair as URLs de todos os erros 404 de 1 GB de logs do Nginx:
192.168.1.1 - - [15/Jan/2024:10:30:45 +0800] "GET /api/users/123 HTTP/1.1" 404 512
192.168.1.2 - - [15/Jan/2024:10:30:46 +0800] "POST /api/login HTTP/1.1" 200 1024
192.168.1.3 - - [15/Jan/2024:10:30:47 +0800] "GET /api/products/456 HTTP/1.1" 404 256
...
Tentar encontrar 404 error all URLs em @.000 linhas vai me deixar com as mãos doloridas—
(2) Solução usando R
library(stringr)
# 1. Read the log
log <- readLines("nginx.log")
# 2. Match using regular expressions 404 Incorrect URL(One line)
pattern <- '"GET (\\S+) HTTP.*" 404'
urls_404 <- str_match(log, pattern)[, 2]
# 3. Track Traffic
url_table <- table(urls_404)
sort(url_table, decreasing = TRUE)[1:10]
Processe um milhão de entradas de log com apenas 3 linhas de código. Esse é o poder das expressões regulares.
3. Sintaxe básica de expressões regulares
(1) Os Quatro Elementos
graph TB
A[Regular Expressions] --> B[Literal characters<br/>abc 123]
A --> C[Metacharacter<br/>. \\d \\s]
A --> D[Measure Words<br/>* + ? {n}]
A --> E[Anchor<br/>^ $ \\b]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#e1d4ff
(2) Caracteres literais
# Exact match of literal characters
"cat" # Matches "cat"
"123" # Matches "123"
(3) Metacaracteres (Núcleo)
| Metacaractere | Significado | Exemplo | Correspondência |
|---|---|---|---|
. |
Qualquer caractere único (exceto quebra de linha) | a.c |
"abc", "axc", "a9c" |
\d |
dígito [0-9] |
\d+ |
"123", "9" |
\D |
não numérico | \D+ |
"abc", "—" |
\s |
espaço (espaço/Tab/quebra de linha) | \s+ |
" ", "\t" |
\S |
não-espaço | \S+ |
“abc” |
\w |
Caracteres alfanuméricos e sublinhados | \w+ |
"abc_123" |
\W |
não- \w |
\W+ |
"—", "@" |
[abc] |
Caractere (qualquer um entre a, b ou c) | [aeiou] |
Qualquer vogal |
[^abc] |
Não é caractere | [^0-9] |
Não é numérico |
[a-z] |
Intervalo | [a-z] |
Qualquer letra minúscula |
\ é uma sequência de escape; portanto, a expressão regular \d deve ser escrita como "\\d" no R.
(4) Palavras de medida
| Palavra de medida | Significado | Exemplo | Correspondência |
|---|---|---|---|
* |
0 vezes ou mais | ab* |
“a”, “ab”, “abb” |
+ |
1 ou mais vezes | ab+ |
“ab”, “abb” (não corresponde a “a”) |
? |
0 ou 1 vez | ab? |
“a”, “ab” |
{n} |
exatamente n vezes | \d{3} |
"123" |
{n,} |
Pelo menos n vezes | \d{2,} |
“12”, “1234” |
{n,m} |
n a m vezes | \d{2,4} |
"12", "1234" |
(5) Âncora
| Anchor | Significado | Exemplo | Correspondência |
|---|---|---|---|
^ |
Início da sequência | ^Hello |
“Olá...” |
$ |
Fim da sequência | World$ |
"...Mundo" |
\b |
Limite de palavra | \bcat\b |
“cat” (não corresponde a “concatenate”) |
4. Agrupamento e captura
(1) Grupo de captura (...)
# Extract the year, month, and day of the date
str_match("2024-01-15", "(\\d{4})-(\\d{2})-(\\d{2})")
# [,1] [,2] [,3] [,4]
# [1,] "2024-01-15" "2024" "01" "15"
# The first column is an exact match,Below are the capture groups
(2) Referência anterior \1
# Match Duplicate Words(e.g. "the the")
str_detect("the the cat", "\\b(\\w+)\\s+\\1\\b")
# [1] TRUE
# Match XML/HTML Tags
str_detect("<div>content</div>", "<(\\w+)>.*</\\1>")
# [1] TRUE
(3) Grupo sem captura (?:...)
# Uncaptured groups(Use ?:)
str_match("John, Smith", "(\\w+)(?:,\\s+)(\\w+)")
# [,1] [,2] [,3]
# "John, Smith" "John" "Smith" ← Commas and spaces are not grouped separately
5. Algoritmo ganancioso x não ganancioso
(1) Algoritmo Greedy Padrão
# Default Greedy:.* Match as many as possible
str_match("<b>text1</b><b>text2</b>", "<b>(.*)</b>")
# [,1] [,2]
# "<b>text1</b><b>text2</b>" "text1</b><b>text2"
# ↑ Greedy matching until the last one </b>
(2) Não ganancioso *?
# Add ? to change to "non-greedy":.*? Match as few as possible
str_match("<b>text1</b><b>text2</b>", "<b>(.*?)</b>")
# [,1] [,2]
# "<b>text1</b>" "text1" ← Matched the first one </b>
6. A função de expressão regular stringr
(1) Tabela de referência rápida de funções
| Função | Finalidade | Retorno |
|---|---|---|
str_detect() |
Correspondência | Vetor lógico |
str_extract() |
Extrair a primeira correspondência | Vetor |
str_extract_all() |
Extrair todas as ocorrências | Lista |
str_match() |
Extrair a primeira correspondência + grupo de captura | Matriz |
str_match_all() |
Extrair todas as correspondências + grupos de captura | Lista |
str_replace() |
Substituir a primeira ocorrência | Vector |
str_replace_all() |
Substituir todas as ocorrências | Vector |
str_split() |
Dividir por partida | Lista |
(2) Detecção com str_detect()
# Which strings start with "a" Introduction
str_detect(c("apple", "banana", "cherry"), "^a")
# [1] TRUE FALSE FALSE
# Contains numbers
str_detect(c("abc", "a1c", "123"), "\\d")
# [1] FALSE TRUE TRUE
(3) Extração com str_extract()
# Extract the number
str_extract(c("abc 123", "def 456", "no digits"), "\\d+")
# [1] "123" "456" NA
# Extract all numbers(List)
str_extract_all(c("a1b2c3", "no digits"), "\\d")
# [[1]] "1" "2" "3"
# [[2]] character(0)
(4) str_match() – Grupos de captura
# Extraction Date: Year/Month/Day
str_match("2024-01-15", "(\\d{4})-(\\d{2})-(\\d{2})")
# [,1] [,2] [,3] [,4]
# [1,] "2024-01-15" "2024" "01" "15"
# Vectorization
dates <- c("2024-01-15", "2024-02-20", "2024-03-25")
str_match(dates, "(\\d{4})-(\\d{2})-(\\d{2})")
# [,1] [,2] [,3] [,4]
# [1,] "2024-01-15" "2024" "01" "15"
# [2,] "2024-02-20" "2024" "02" "20"
# [3,] "2024-03-25" "2024" "03" "25"
(5) Substituição com str_replace_all()
# Replace all numbers with X
str_replace_all("abc 123 def 456", "\\d+", "X")
# [1] "abc X def X"
# Complex Replacements:Using capture groups
str_replace_all("John Smith", "(\\w+) (\\w+)", "\\2, \\1")
# [1] "Smith, John" ← Last Name First
7. Prática: Padrões comuns de validação
(1) Várias expressões regulares de validação
# Cell phone number
phone_pat <- "^1[3-9]\\d{9}$"
# Email
email_pat <- "^[\\w.+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$"
# ID Number(18 bit)
id_pat <- "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$"
# URL
url_pat <- "^https?://[\\w.-]+(:\\d+)?(/\\S*)?$"
# IPv4
ipv4_pat <- "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$"
# Date
date_pat <- "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$"
# Test
str_detect("13800001234", phone_pat) # TRUE
str_detect("user@example.com", email_pat) # TRUE
str_detect("110101199003078888", id_pat) # TRUE
str_detect("https://example.com", url_pat) # TRUE
(2) Extraia as partes da URL
url <- "https://www.example.com:8080/path/to/page?query=1#section"
# Extraction Agreement,Domain Name,Port,Path,Parameters
pattern <- "^(https?)://([^:/]+)(?::(\\d+))?(/[^?#]*)?(?:\\?([^#]*))?(?:#(.*))?$"
matches <- str_match(url, pattern)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# "https://..." "https" "www.example.com" "8080" "/path/to/page" "query=1" "section"
8. Prática: Analisando os logs do Nginx
A seguir, apresentamos um exemplo de um fluxo de trabalho completo que reúne todos os conceitos de expressões regulares abordados nesta aula.
▶ Exemplo: Análise dos logs de acesso do Nginx
# ============================================
# Nginx Access Log Analysis
# Features:Parse using regular expressions @,000 rows ofLog,Key Statistical Indicators
# ============================================
library(stringr)
library(dplyr)
# 1. Simulation 1000 row Nginx Log(For use in actual projects readLines Read File)
set.seed(42)
n <- 1000
ips <- paste0("192.168.", sample(1:255, n, replace = TRUE), ".",
sample(1:255, n, replace = TRUE))
methods <- sample(c("GET", "POST", "PUT", "DELETE"), n, replace = TRUE,
prob = c(0.7, 0.2, 0.05, 0.05))
paths <- paste0("/api/", sample(c("users", "products", "orders", "login"),
n, replace = TRUE), "/",
sample(1:1000, n, replace = TRUE))
status_codes <- sample(c(200, 201, 301, 400, 404, 500), n, replace = TRUE,
prob = c(0.7, 0.05, 0.05, 0.05, 0.1, 0.05))
sizes <- sample(100:5000, n)
# Concatenate into log lines
log_lines <- sprintf(
'%s - - [15/Jan/2024:10:30:%02d +0800] "%s %s HTTP/1.1" %d %d',
ips, sample(0:59, n, replace = TRUE), methods, paths, status_codes, sizes
)
# 2. Write to the log file
writeLines(log_lines, "nginx_demo.log")
cat("Generated 1000 Transaction Log\n")
# 3. Parsing Logs with Regular Expressions
# Format:IP - - [Time] "Methods URL Agreement" Status Code Size
log_pattern <- '^(\\S+) - - \\[([^\\]]+)\\] "(\\S+) (\\S+) (\\S+)" (\\d+) (\\d+)$'
# Extract all capture groups
matches <- str_match(log_lines, log_pattern)
colnames(matches) <- c("full", "ip", "time", "method", "path", "protocol",
"status", "size")
# Rotate DataFrame
logs <- as_tibble(matches) |> select(-full) |>
mutate(
status = as.integer(status),
size = as.integer(size)
)
cat("\n=== First 6 Analysis results (entries) ===\n")
print(head(logs, 6))
# 4. Analysis 1:Status Code Distribution
cat("\n=== Status Code Distribution ===\n")
status_summary <- logs |>
group_by(status) |>
summarise(Number of times = n(), Percentage = round(n() / nrow(logs) * 100, 2)) |>
arrange(desc(Number of times))
print(status_summary)
# 5. Analysis 2:Find 404 All Errors URL
cat("\n=== 404 Error Top 5 ===\n")
errors_404 <- logs |>
filter(status == 404) |>
group_by(path) |>
summarise(Number of times = n()) |>
arrange(desc(Number of times)) |>
head(5)
print(errors_404)
# 6. Analysis 3:Each IP traffic
cat("\n=== Top 5 Active IP ===\n")
top_ips <- logs |>
group_by(ip) |>
summarise(
Number of visits = n(),
Number of errors = sum(status >= 400),
Total Traffic = sum(size)
) |>
arrange(desc(Number of visits)) |>
head(5)
print(top_ips)
# 7. Analysis 4:Hourly traffic
cat("\n=== Visits per minute(First 10 minutes)===\n")
logs <- logs |>
mutate(minute = str_sub(time, 15, 16))
per_minute <- logs |>
group_by(minute) |>
summarise(Number of visits = n(), Number of errors = sum(status >= 400)) |>
head(10)
print(per_minute)
# 8. Retrieve Email Address(If the log contains an email address)
text_with_emails <- "Contact alice@example.com or bob@test.org for support."
email_pat <- "[\\w.+-]+@[\\w.-]+\\.[a-zA-Z]{2,}"
emails <- str_extract_all(text_with_emails, email_pat)
cat("\n=== Extracted email addresses ===\n")
print(emails)
# 9. Cleanup
file.remove("nginx_demo.log")
Resultado esperado (trecho):
=== Status Code Distribution ===
# A tibble: 6 × 3
status Number of times Percentage
<int> <int> <dbl>
1 200 698 70
2 404 105 10
3 201 52 5
...
=== Top 5 Active IP ===
# A tibble: 5 × 4
ip Number of visits Number of errors Total Traffic
<chr> <int> <int> <int>
1 192.168.97.1 8 1 18499
2 192.168.52.244 7 2 18438
...
❓ Perguntas Frequentes
P: As expressões regulares do R seguem o padrão POSIX ou o estilo Perl? R: O
stringrutiliza a biblioteca de expressões regulares ICU (semelhante ao Perl) e suporta as sintaxes abreviadas\d,\se\w. O R padrãogreputiliza as expressões regulares POSIX (ERE) ([[:digit:]]). Recomenda-se o estilostringr.
P:
\\dPor que há duas barras invertidas? R: A string do R"\\d"é, na verdade, uma expressão regular\d— o R interpreta\\como um único\. Da mesma forma,"\\s"é, na verdade, uma expressão regular\s.
P: Como faço para escolher entre correspondência gananciosa e não gananciosa? R: O padrão é a correspondência gananciosa, que corresponde ao número máximo de caracteres. Isso pode causar problemas ao analisar HTML, XML ou JSON; adicione
?para mudar para a correspondência não gananciosa. Para correspondências simples, a correspondência gananciosa é suficiente.
P: Qual é a diferença entre
str_matchestr_extract? R:str_extractretorna apenas a primeira correspondência;str_matchretorna a correspondência completa, além de cada grupo de captura. Usestr_matchpara recuperar os grupos de captura.
P: Como faço para lidar com caracteres chineses? R: No R 4.x UTF-8, use diretamente os literais
[\u4e00-\u9fff]ou"Chinese". Para arquivos GBK, converta-os primeiro para UTF-8.
P: O que posso fazer se a correspondência de expressões regulares estiver lenta? **R: ① Evite a avidez do
.*(use o.*?) ② Pré-compile padrões complexos ③ Use ostringi::stri_detect_regex()para obter um desempenho mais rápido (backend em C++) |
📖 Resumo
- As expressões regulares são uma linguagem de correspondência de padrões composta por caracteres literais, metacaracteres, quantificadores e âncoras.
- Os metacaracteres
.\d\s\w^$\bconstituem o núcleo - Em uma string R,
\é uma sequência de escape; em uma expressão regular,\dé escrito como"\\d" - 4 Palavras múltiplas
*+?{n,m}Controlar o número de repetições - Grupo de captura
(...)+ referência retroativa\1para extração e reutilização - funções de expressão regular do stringr:
str_detect/str_extract/str_match/str_replace_all/str_split - Correspondência gananciosa da sequência mais longa; adicione
?para torná-la não gananciosa - Análise de HTML/XML Não use expressões regulares — Use o analisador xml2
📝 Exercícios
-
Problema básico: Use uma expressão regular para extrair todos os valores (no formato
"Price: $99.50, Quantity: 3 pcs, Total: $298.50") da sequência de caracteres"Price: $99.50, Quantity: 3 pcs, Total: $298.50"e gere uma lista de números. -
Problema básico: Use uma expressão regular para verificar se os dados a seguir estão de acordo com o formato de e-mail: ①
user@example.com②user.name+tag@sub.example.co.uk③invalid@④@example.com. Gere um vetor booleano. -
Problema básico: Use uma expressão regular para extrair todas as datas de
"Today is 2024-01-15, weather Sunny; Tomorrow is 2024-01-16, Cloudy"e gerar um vetor de caracteres. -
Exercício avançado: Simule 100 “e-mails de confirmação de pedido” (contendo números de pedido, valores, datas e nomes de produtos), use expressões regulares para extrair esses quatro campos para um data frame e verifique se cada um deles foi extraído com sucesso.
-
Desafio: Escreva um script para ler 1.000 linhas dos logs de acesso do Nginx e use expressões regulares para: ① analisar os campos de cada linha (IP/hora/método/URL/código de status/tamanho); ② listar as 10 principais URLs com erros 4xx; ③ identificar o endereço IP com a maior frequência de acesso; ④ calcular a média de tráfego por hora. Faça uma captura de tela e salve os resultados da análise.