R: R والتعبيرات النمطية
آخر تحديث: 2026-08-26
في الدرس السابق، تعلمنا أساسيات معالجة السلاسل النصية، لكن 80% من السيناريوهات الواقعية تتطلب مطابقة الأنماط — ليس مطابقة سلاسل نصية ثابتة، بل مطابقة «سلاسل نصية تتبع نمطًا معينًا». وهذه هي قوة التعبيرات النمطية (regex).
بعد الانتهاء من هذا الدرس، ستتمكن من استخدام التعبيرات النمطية في لغة R من أجل: التحقق من صحة أرقام الهواتف، واستخراج عناوين البريد الإلكتروني، وتحليل عناوين URL، وتحليل السجلات، وإجراء التنقيب في النصوص.
1. ما ستتعلمه
- أساسيات التعبيرات النمطية (الأحرف، المحددات الكمية، نقاط الربط، المجموعات)
- R: قواعد بناء التعبيرات النمطية (نمط POSIX/Perl)
- وظائف التعبيرات النمطية في stringr (str_match/str_extract/str_detect)
- مجموعات الالتقاط والإشارات الرجعية
- المطابقة الجشعة مقابل المطابقة غير الجشعة
- دليل عملي: التحقق عبر رقم الهاتف والبريد الإلكتروني وعنوان URL ورقم الهوية
2. قصة عن تحليل السجلات
(1) المشكلة: ماذا نفعل مع مليون سجل؟
أليس هي مهندسة عمليات تحتاج إلى استخراج عناوين URL لجميع أخطاء 404 من سجلات Nginx التي يبلغ حجمها 1 جيجابايت:
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
...
محاولة العثور على 404 error all URLs في @,000 صف ستجعل يدي تتعب—
(2) الحل باستخدام لغة 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]
معالجة مليون إدخال سجل بثلاثة أسطر فقط من التعليمات البرمجية. هذه هي قوة التعبيرات النمطية.
3. قواعد بناء العبارات النمطية الأساسية
(1) العناصر الأربعة
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) الأحرف الحرفية
# Exact match of literal characters
"cat" # Matches "cat"
"123" # Matches "123"
(3) الأحرف الخاصة (الأساسية)
| الحرف الخاص | المعنى | مثال | المطابقة |
|---|---|---|---|
. |
أي حرف واحد (باستثناء حرف نهاية السطر) | a.c |
"abc"، "axc"، "a9c" |
\d |
رقم [0-9] |
\d+ |
"123"، "9" |
\D |
غير رقمي | \D+ |
"abc"، "—" |
\s |
مسافة (مسافة/مفتاح Tab/فاصل أسطر) | \s+ |
" "، "\t" |
\S |
غير الفراغ | \S+ |
"abc" |
\w |
الأحرف الأبجدية الرقمية وعلامات التسطير | \w+ |
"abc_123" |
\W |
غير \w |
\W+ |
"—"، "@" |
[abc] |
حرف (أي من a أو b أو c) | [aeiou] |
أي حرف متحرك |
[^abc] |
غير حرفي | [^0-9] |
غير رقمي |
[a-z] |
النطاق | [a-z] |
أي حرف صغير |
\ تسلسلاً للهروب، لذا يجب كتابة التعبير النمطي \d على النحو التالي: "\\d" في R.
(4) كلمات القياس
| كلمة القياس | المعنى | مثال | المطابقة |
|---|---|---|---|
* |
0 مرة أو أكثر | ab* |
"a"، "ab"، "abb" |
+ |
مرة واحدة أو أكثر | ab+ |
"ab"، "abb" (لا تتطابق مع "a") |
? |
0 أو 1 مرة | ab? |
"a"، "ab" |
{n} |
n مرة بالضبط | \d{3} |
"123" |
{n,} |
n مرة على الأقل | \d{2,} |
"12"، "1234" |
{n,m} |
من n إلى m مرة | \d{2,4} |
"12"، "1234" |
(5) المرساة
| المصطلح | المعنى | مثال | المطابقة |
|---|---|---|---|
^ |
بداية السلسلة | ^Hello |
"مرحبًا..." |
$ |
نهاية السلسلة | World$ |
"...العالم" |
\b |
حدود الكلمة | \bcat\b |
"cat" (لا تتطابق مع "concatenate") |
4. التجميع والتسجيل
(1) مجموعة التقاط (...)
# 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) الإشارة العكسية \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) المجموعة غير الملتقطة (?:...)
# 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. الخوارزمية الجشعة مقابل الخوارزمية غير الجشعة
(1) الخوارزمية الجشعة الافتراضية
# 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) غير الجشع *?
# 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. دالة التعبير النمطي stringr
(1) جدول مرجعي سريع للوظائف
| الدالة | الغرض | القيمة المرجعة |
|---|---|---|
str_detect() |
المطابقة | المتجه المنطقي |
str_extract() |
استخراج أول نتيجة مطابقة | متجه |
str_extract_all() |
استخراج جميع النتائج المطابقة | قائمة |
str_match() |
استخراج أول تطابق + مجموعة التقاط | مصفوفة |
str_match_all() |
استخراج جميع التطابقات + مجموعات الالتقاط | قائمة |
str_replace() |
استبدال أول نتيجة مطابقة | متجه |
str_replace_all() |
استبدال جميع النتائج المطابقة | Vector |
str_split() |
تقسيم حسب المباراة | قائمة |
(2) الكشف باستخدام 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) الاستخراج باستخدام 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() المجموعات الملتقطة
# 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) الاستبدال باستخدام 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. تدريب عملي: أنماط التحقق الشائعة
(1) تعبيرات منتظمة متنوعة للتحقق من الصحة
# 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) استخراج أجزاء عنوان 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. تدريب عملي: تحليل سجلات Nginx
فيما يلي مثال على مسار عمل كامل يربط بين جميع مفاهيم التعبيرات النمطية التي تم تناولها في هذا الدرس.
▶ مثال: تحليل سجلات الوصول في 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")
النتائج المتوقعة (مقتطف):
=== 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
...
❓ أسئلة شائعة
\\d لماذا توجد شرشتان مائلتان؟"\\d" في لغة R هي في الواقع تعبير عادي \d — حيث تقوم لغة R بتحليل \\ على أنها \ واحد. وبالمثل، فإن "\\s" هي في الواقع تعبير عادي \s.[\u4e00-\u9fff] أو "Chinese" مباشرةً. أما بالنسبة لملفات GBK، فقم بتحويلها إلى UTF-8 أولاً.📖 ملخص
- التعبيرات النمطية هي لغة لمطابقة الأنماط تتألف من أحرف حرفية، وأحرف خاصة، ومحددات الكمية، ونقاط ربط.
- الأحرف الخاصة
.\d\s\w^$\bهي العناصر الأساسية - في سلسلة R، يُعد
\تسلسلاً للهروب؛ أما في التعبير النمطي، فيُكتب\dعلى النحو التالي:"\\d" - 4 كلمات متعددة
*+?{n,m}التحكم في عدد مرات التكرار - مجموعة التقاط
(...)+ الإشارة العكسية\1للاستخراج وإعادة الاستخدام - وظائف التعبيرات النمطية في stringr:
str_detect/str_extract/str_match/str_replace_all/str_split - المطابقة الجشعة للسلسلة الأطول؛ أضف
?لجعلها غير جشعة - تحليل HTML/XML لا تستخدم التعبيرات النمطية — استخدم محلل xml2
📝 تمارين
-
المشكلة الأساسية: استخدم تعبيرًا منتظمًا لاستخراج جميع المبالغ (بالصيغة
"Price: $99.50, Quantity: 3 pcs, Total: $298.50") من السلسلة"Price: $99.50, Quantity: 3 pcs, Total: $298.50"، ثم أخرج قائمة بالأرقام. -
المشكلة الأساسية: استخدم تعبيرًا منتظمًا للتحقق مما إذا كانت البيانات التالية تتوافق مع تنسيق البريد الإلكتروني: ①
user@example.com②user.name+tag@sub.example.co.uk③invalid@④@example.com. أخرج متجهًا منطقيًّا. -
المشكلة الأساسية: استخدم تعبيرًا منتظمًا لاستخراج جميع التواريخ من
"Today is 2024-01-15, weather Sunny; Tomorrow is 2024-01-16, Cloudy"وإخراج متجه أحرف. -
تمرين متقدم: قم بمحاكاة 100 «رسالة بريد إلكتروني لتأكيد الطلب» (تحتوي على أرقام الطلبات، والمبالغ، والتواريخ، وأسماء المنتجات)، واستخدم التعبيرات النمطية لاستخراج هذه الحقول الأربعة إلى إطار بيانات، وتأكد من استخراج كل منها بنجاح.
-
التحدي: اكتب برنامجًا نصيًّا لقراءة 1,000 سطر من سجلات الوصول الخاصة بـ Nginx واستخدم التعبيرات النمطية من أجل: ① تحليل الحقول الموجودة في كل سطر (عنوان IP/الوقت/الطريقة/عنوان URL/رمز الحالة/الحجم)؛ ② سرد أهم 10 عناوين URL التي تحتوي على أخطاء 4xx؛ ③ تحديد عنوان IP الذي سجل أعلى معدل تكرار للوصول؛ ④ حساب متوسط حركة المرور لكل ساعة. التقط لقطة شاشة واحفظ نتائج التحليل.