R: R والتعبيرات النمطية

آخر تحديث: 2026-08-26

في الدرس السابق، تعلمنا أساسيات معالجة السلاسل النصية، لكن 80% من السيناريوهات الواقعية تتطلب مطابقة الأنماط — ليس مطابقة سلاسل نصية ثابتة، بل مطابقة «سلاسل نصية تتبع نمطًا معينًا». وهذه هي قوة التعبيرات النمطية (regex).

بعد الانتهاء من هذا الدرس، ستتمكن من استخدام التعبيرات النمطية في لغة R من أجل: التحقق من صحة أرقام الهواتف، واستخراج عناوين البريد الإلكتروني، وتحليل عناوين URL، وتحليل السجلات، وإجراء التنقيب في النصوص.

1. ما ستتعلمه



2. قصة عن تحليل السجلات

(1) المشكلة: ماذا نفعل مع مليون سجل؟

أليس هي مهندسة عمليات تحتاج إلى استخراج عناوين URL لجميع أخطاء 404 من سجلات Nginx التي يبلغ حجمها 1 جيجابايت:

TEXT 📖 للعرض فقط
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

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) العناصر الأربعة

100%
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) الأحرف الحرفية

R
# 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] أي حرف صغير
⚠️ ملاحظة: في سلاسل R، يُعد \ تسلسلاً للهروب، لذا يجب كتابة التعبير النمطي \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) مجموعة التقاط (...)

R
# 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

R
# 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) المجموعة غير الملتقطة (?:...)

R
# 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) الخوارزمية الجشعة الافتراضية

R
# 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) غير الجشع *?

R
# 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>
💡 نصيحة: لا تستخدم التعبيرات النمطية لتحليل HTML — استخدم محللًا مخصصًا (xml2، الدرس 13). التعبيرات النمطية هي الأنسب لمطابقة الأنماط البسيطة.



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()

R
# 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()

R
# 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() المجموعات الملتقطة

R
# 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()

R
# 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) تعبيرات منتظمة متنوعة للتحقق من الصحة

R
# 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

R
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

R 📖 للعرض فقط
# ============================================
# 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")
70 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

النتائج المتوقعة (مقتطف):

TEXT 📖 للعرض فقط
=== 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.
س كيف أتعامل مع الأحرف الصينية؟
ج في R 4.x UTF-8، استخدم القيم الثابتة [\u4e00-\u9fff] أو "Chinese" مباشرةً. أما بالنسبة لملفات GBK، فقم بتحويلها إلى UTF-8 أولاً.

📖 ملخص


📝 تمارين

  1. المشكلة الأساسية: استخدم تعبيرًا منتظمًا لاستخراج جميع المبالغ (بالصيغة "Price: $99.50, Quantity: 3 pcs, Total: $298.50") من السلسلة "Price: $99.50, Quantity: 3 pcs, Total: $298.50"، ثم أخرج قائمة بالأرقام.

  2. المشكلة الأساسية: استخدم تعبيرًا منتظمًا للتحقق مما إذا كانت البيانات التالية تتوافق مع تنسيق البريد الإلكتروني: ① user@example.comuser.name+tag@sub.example.co.ukinvalid@@example.com. أخرج متجهًا منطقيًّا.

  3. المشكلة الأساسية: استخدم تعبيرًا منتظمًا لاستخراج جميع التواريخ من "Today is 2024-01-15, weather Sunny; Tomorrow is 2024-01-16, Cloudy" وإخراج متجه أحرف.

  4. تمرين متقدم: قم بمحاكاة 100 «رسالة بريد إلكتروني لتأكيد الطلب» (تحتوي على أرقام الطلبات، والمبالغ، والتواريخ، وأسماء المنتجات)، واستخدم التعبيرات النمطية لاستخراج هذه الحقول الأربعة إلى إطار بيانات، وتأكد من استخراج كل منها بنجاح.

  5. التحدي: اكتب برنامجًا نصيًّا لقراءة 1,000 سطر من سجلات الوصول الخاصة بـ Nginx واستخدم التعبيرات النمطية من أجل: ① تحليل الحقول الموجودة في كل سطر (عنوان IP/الوقت/الطريقة/عنوان URL/رمز الحالة/الحجم)؛ ② سرد أهم 10 عناوين URL التي تحتوي على أخطاء 4xx؛ ③ تحديد عنوان IP الذي سجل أعلى معدل تكرار للوصول؛ ④ حساب متوسط حركة المرور لكل ساعة. التقط لقطة شاشة واحفظ نتائج التحليل.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%