Regular Expressions (RegEx) in Python
fungsi-fungsi Python RegEx secara rinci, termasuk metacharacters dan special sequences, serta memberikan contoh implementasi dalam industri.
- Metacharacters dalam RegEx:
- . (titik): Mencocokkan karakter apapun kecuali newline.
- ^ (caret): Mencocokkan awal string.
- $ (dollar): Mencocokkan akhir string.
-
- * (asterisk): Mencocokkan 0 atau lebih kemunculan karakter sebelumnya.
-
- + (plus): Mencocokkan 1 atau lebih kemunculan karakter sebelumnya.
- ? (question mark): Mencocokkan 0 atau 1 kemunculan karakter sebelumnya.
- {} (curly braces): Menentukan jumlah kemunculan karakter sebelumnya.
- [] (square brackets): Mencocokkan satu karakter dari set karakter.
- | (pipe): Mencocokkan salah satu dari beberapa alternatif.
- () (parentheses): Mengelompokkan pola dan membuat grup.
- \ (backslash): Menghilangkan arti khusus dari karakter metacharacter.
- Special Sequences dalam RegEx:
- \d: Mencocokkan digit (0-9).
- \D: Mencocokkan karakter non-digit [^0-9]..
- \w: Mencocokkan karakter word (a-z, A-Z, 0-9, _).[a-zA-Z0-9]
- \W: Mencocokkan karakter non-word. [a-zA-Z0-9]
- \s: Mencocokkan whitespace (spasi, tab, newline) [\t\n\r\f\v]
- \S: Mencocokkan karakter non-whitespace. [^\t\n\r\f\v]
- \b: Mencocokkan batas kata.
- \B: Mencocokkan posisi yang bukan batas kata.
3. RegEx Functions in Python
- re.match(): Periksa untuk pertandingan hanya di awal string.
- re.search(): Pencarian untuk munculnya pola pertama dalam string.
- re.findall(): Mengembalikan daftar yang berisi semua pertandingan.
- re.finditer(): Mengembalikan iterator yang menghasilkan objek yang cocok.
- re.split(): Membagi string dengan kejadian pola.
- re.sub(): Mengganti satu atau lebih pertandingan dengan string.
Contoh implementasi RegEx dalam industri:
- Validasi format data input:
a. Validasi nomor telepon Indonesia:
pythonimport re def validate_phone_number(phone): pattern = r'^(\+62|62|0)8[1-9][0-9]{7,10}$' return bool(re.match(pattern, phone)) # Contoh penggunaan print(validate_phone_number('+6281234567890')) # True print(validate_phone_number('081234567890')) # True print(validate_phone_number('8123456789')) # False
Penjelasan:
^
dan$
memastikan pola cocok dari awal hingga akhir string.(\+62|62|0)
mencocokkan kode negara atau awalan '0'.8
mencocokkan digit pertama nomor seluler Indonesia.[1-9]
mencocokkan digit kedua (1-9).[0-9]{7,10}
mencocokkan 7 hingga 10 digit terakhir.
Extracting Phone Numbers
pythonimport redef extract_phone_numbers(text):pattern = r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b'return re.findall(pattern, text)text = "Contact us at 123-456-7890 or 987.654.3210"phone_numbers = extract_phone_numbers(text)print(phone_numbers) # Output: ['123-456-7890', '987.654.3210']
b. Validasi alamat email:
pythonimport re def validate_email(email): pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return bool(re.match(pattern, email)) # Contoh penggunaan print(validate_email('user@example.com')) # True print(validate_email('invalid.email@')) # False
Penjelasan:
[a-zA-Z0-9._%+-]+
mencocokkan username email.@
mencocokkan karakter '@'.[a-zA-Z0-9.-]+
mencocokkan domain name.\.[a-zA-Z]{2,}
mencocokkan top-level domain (minimal 2 karakter).
Validating Email Addresses
python
import re
def validate_email(email): pattern = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' if re.match(pattern, email): return True return False
emails = ['test@example.com', 'invalid-email', 'user@domain.co']
valid_emails = [email for email in emails if validate_email(email)]print(valid_emails) # Output: ['test@example.com', 'user@domain.co']
- Ekstraksi informasi dari teks tidak terstruktur:
pythonimport re def extract_dates(text): pattern = r'\b(\d{1,2}/\d{1,2}/\d{4}|\d{1,2}-\d{1,2}-\d{4})\b' return re.findall(pattern, text) # Contoh penggunaan report = """ Laporan Keuangan Triwulan I 2024 Tanggal: 15/04/2024 Periode: 01-01-2024 sampai 31-03-2024 """ dates = extract_dates(report) print(dates) # ['15/04/2024', '01-01-2024', '31-03-2024']
Penjelasan:
- Pattern mencocokkan format tanggal DD/MM/YYYY atau DD-MM-YYYY.
\b
memastikan tanggal adalah kata terpisah.\d{1,2}
mencocokkan 1 atau 2 digit untuk hari dan bulan.\d{4}
mencocokkan 4 digit untuk tahun.
- Pencocokan dan penggantian pola teks:
pythonimport re def anonymize_data(text): # Anonymize email addresses text = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '[EMAIL]', text) # Anonymize phone numbers text = re.sub(r'\b(\+62|62|0)8[1-9][0-9]{7,10}\b', '[PHONE]', text) return text # Contoh penggunaan original_text = """ Nama: John Doe Email: john.doe@example.com Telepon: +6281234567890 """ anonymized_text = anonymize_data(original_text) print(anonymized_text)
Output:
Nama: John Doe Email: [EMAIL] Telepon: [PHONE]
Penjelasan:
re.sub()
digunakan untuk mengganti pola yang cocok dengan teks pengganti.- Pattern email dan nomor telepon sama seperti contoh sebelumnya.
4. Cleaning Up Text Data
pythonimport redef clean_text(text):# Remove HTML tagstext = re.sub(r'<.*?>', '', text)# Remove non-alphanumeric characterstext = re.sub(r'[^a-zA-Z0-9\s]', '', text)# Remove extra whitespacetext = re.sub(r'\s+', ' ', text).strip()return texthtml_text = "<p>This is a <b>bold</b> move.</p>"cleaned_text = clean_text(html_text)print(cleaned_text) # Output: "This is a bold move"
5. Parsing Logs
pythonimport redef parse_logs(log):pattern = r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) (.*)'match = re.search(pattern, log)if match:return {'date': match.group(1),'time': match.group(2),'level': match.group(3),'message': match.group(4)}return Nonelog_entry = "2023-07-03 14:23:07 ERROR Something went wrong."parsed_log = parse_logs(log_entry)print(parsed_log)# Output: {'date': '2023-07-03', 'time': '14:23:07', 'level': 'ERROR', 'message': 'Something went wrong.'}
Implementasi ini dapat digunakan dalam industri untuk:
- Meningkatkan keamanan data dengan mengaburkan informasi sensitif sebelum menyimpan atau mentransmisikan data.
- Memastikan konsistensi format data input, mengurangi kesalahan dan meningkatkan kualitas data.
- Mengotomatisasi ekstraksi informasi penting dari dokumen tidak terstruktur, menghemat waktu dan mengurangi kesalahan manual.
- Memudahkan analisis data dengan mengekstrak dan menstandarisasi format informasi tertentu dari berbagai sumber.
Dengan menggunakan RegEx, perusahaan dapat meningkatkan efisiensi operasional, mengurangi risiko kesalahan manusia, dan memproses data dengan lebih cepat dan akurat.
Tambahan :
- Ekstraksi informasi dari log server:
Misalkan kita memiliki log server dengan format sebagai berikut:
[2024-03-15 08:45:32] INFO: User 'johndoe' logged in from IP 192.168.1.100 [2024-03-15 09:12:45] ERROR: Database connection failed [2024-03-15 10:30:18] WARNING: High CPU usage detected (85%)
Kita dapat menggunakan RegEx untuk mengekstrak informasi penting dari log ini:
pythonimport re def parse_log(log_text): pattern = r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+)' entries = [] for line in log_text.split('\n'): match = re.match(pattern, line) if match: timestamp, level, message = match.groups() entries.append({ 'timestamp': timestamp, 'level': level, 'message': message }) return entries # Contoh penggunaan log_text = """ [2024-03-15 08:45:32] INFO: User 'johndoe' logged in from IP 192.168.1.100 [2024-03-15 09:12:45] ERROR: Database connection failed [2024-03-15 10:30:18] WARNING: High CPU usage detected (85%) """ parsed_logs = parse_log(log_text) for entry in parsed_logs: print(f"Time: {entry['timestamp']}, Level: {entry['level']}, Message: {entry['message']}")
Penjelasan:
- Pattern
\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]
mencocokkan timestamp dalam kurung siku. (\w+):
mencocokkan level log (INFO, ERROR, WARNING).(.+)
mencocokkan sisa pesan log.re.match()
digunakan untuk mencocokkan pola dari awal string.match.groups()
mengembalikan tuple dari grup yang cocok.
- Validasi dan ekstraksi URL:
pythonimport re def extract_urls(text): pattern = r'https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)' return re.findall(pattern, text) def validate_url(url): pattern = r'^https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)$' return bool(re.match(pattern, url)) # Contoh penggunaan text = "Kunjungi website kami di https://www.example.com atau http://example.org untuk informasi lebih lanjut." urls = extract_urls(text) print("URLs ditemukan:", urls) for url in urls: print(f"URL '{url}' valid: {validate_url(url)}")
Penjelasan pattern URL:
https?://
mencocokkan "http://" atau "https://".(?:www\.)?
mencocokkan "www." opsional.[-a-zA-Z0-9@:%._\+~#=]{1,256}
mencocokkan nama domain.\.[a-zA-Z0-9()]{1,6}
mencocokkan top-level domain.\b
memastikan batas kata.(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)
mencocokkan path dan query string opsional.
- Pemrosesan dan normalisasi teks:
pythonimport re def normalize_text(text): # Menghapus karakter non-alfanumerik kecuali spasi text = re.sub(r'[^a-zA-Z0-9\s]', '', text) # Mengganti beberapa spasi berturut-turut dengan satu spasi text = re.sub(r'\s+', ' ', text) # Mengubah teks menjadi lowercase text = text.lower() return text.strip() # Contoh penggunaan original_text = " Ini adalah CONTOH teks yang akan di-normalize!!! " normalized_text = normalize_text(original_text) print(f"Original: '{original_text}'") print(f"Normalized: '{normalized_text}'")
Penjelasan:
[^a-zA-Z0-9\s]
mencocokkan karakter yang bukan alfanumerik atau spasi.\s+
mencocokkan satu atau lebih karakter whitespace.strip()
menghapus whitespace di awal dan akhir string.
- Ekstraksi informasi dari HTML:
pythonimport re def extract_links(html): pattern = r'<a\s+(?:[^>]*?\s+)?href="([^"]*)"' return re.findall(pattern, html) def extract_image_sources(html): pattern = r'<img\s+(?:[^>]*?\s+)?src="([^"]*)"' return re.findall(pattern, html) # Contoh penggunaan html_content = """ <html> <body> <a href="https://www.example.com">Link 1</a> <img src="image1.jpg" alt="Image 1"> <a href="/page2.html">Link 2</a> <img src="https://example.com/image2.png" alt="Image 2"> </body> </html> """ links = extract_links(html_content) images = extract_image_sources(html_content) print("Links:", links) print("Image sources:", images)
Penjelasan:
- Pattern untuk link:
<a\s+(?:[^>]*?\s+)?href="([^"]*)"
<a
mencocokkan tag pembuka anchor.\s+
mencocokkan satu atau lebih whitespace.(?:[^>]*?\s+)?
mencocokkan atribut opsional sebelum href.href="([^"]*)"
mencocokkan atribut href dan mengambil nilainya.
- Pattern untuk gambar:
<img\s+(?:[^>]*?\s+)?src="([^"]*)"
- Mirip dengan pattern link, tetapi untuk tag
<img>
dan atributsrc
.
- Mirip dengan pattern link, tetapi untuk tag
Implementasi-implementasi ini dapat digunakan dalam berbagai industri untuk:
- Analisis log: Memudahkan pemantauan sistem dan troubleshooting dengan cepat mengekstrak informasi penting dari log server.
- Keamanan web: Validasi URL dapat membantu mencegah serangan phishing dan memastikan integritas tautan.
- Pemrosesan bahasa alami: Normalisasi teks membantu dalam analisis sentimen, klasifikasi teks, dan tugas NLP lainnya.
- Web scraping: Ekstraksi informasi dari HTML memungkinkan pengumpulan data otomatis dari situs web.
RegEx sangat kuat dan fleksibel, tetapi juga dapat menjadi kompleks. Penting untuk memahami trade-off antara kompleksitas pattern dan kinerja, terutama ketika bekerja dengan dataset besar. Dalam beberapa kasus, parser HTML atau XML khusus mungkin lebih sesuai daripada RegEx untuk memproses markup yang kompleks.
Tambahan Example :
- Parsing CSV dengan RegEx:
Meskipun Python memiliki modul csv bawaan, RegEx dapat berguna untuk parsing CSV yang memiliki format tidak standar atau kompleks.
pythonimport re def parse_csv(csv_string): pattern = r''' (?:^|,) # Awal baris atau koma (?=[^"]|"(?:[^"]|"")*")? # Look-ahead untuk menentukan apakah field dikutip atau tidak ([^,]+) # Capture grup untuk field tanpa kutip |"([^"]*(?:""[^"]*)*)" # Capture grup untuk field dengan kutip ''' rows = [] for line in csv_string.strip().split('\n'): row = [] for match in re.finditer(pattern, line, re.VERBOSE): field = match.group(1) or match.group(2).replace('""', '"') row.append(field.strip()) rows.append(row) return rows # Contoh penggunaan csv_data = ''' Name,Age,City "Doe, John",30,"New York, NY" "Smith, Jane",25,London ''' parsed_data = parse_csv(csv_data) for row in parsed_data: print(row)
Penjelasan:
- Pola RegEx ini menangani field dengan dan tanpa tanda kutip.
re.VERBOSE
memungkinkan kita menulis pola RegEx yang lebih mudah dibaca dengan komentar.(?:^|,)
adalah non-capturing group yang mencocokkan awal baris atau koma.(?=[^"]|"(?:[^"]|"")*")?
adalah positive lookahead yang menentukan apakah field dikutip atau tidak.([^,]+)
menangkap field tanpa tanda kutip."([^"]*(?:""[^"]*)*)"
menangkap field dengan tanda kutip, termasuk tanda kutip ganda di dalamnya.
- Validasi password yang kompleks:
pythonimport re def validate_password(password): pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$' return bool(re.match(pattern, password)) # Contoh penggunaan passwords = [ "Weak123", "StrongPass1!", "NoUpperCase1!", "NoLowerCase1!", "NoNumber!", "NoSpecial1", "Short1!", ] for password in passwords: print(f"'{password}' is valid: {validate_password(password)}")
Penjelasan pattern:
^
dan$
memastikan pola cocok dari awal hingga akhir string.(?=.*[a-z])
positive lookahead untuk memastikan setidaknya satu huruf kecil.(?=.*[A-Z])
positive lookahead untuk memastikan setidaknya satu huruf besar.(?=.*\d)
positive lookahead untuk memastikan setidaknya satu angka.(?=.*[@$!%*?&])
positive lookahead untuk memastikan setidaknya satu karakter khusus.[A-Za-z\d@$!%*?&]{8,}
memastikan password terdiri dari karakter yang diizinkan dan minimal 8 karakter.
- Ekstraksi dan validasi ISBN:
pythonimport re def extract_and_validate_isbn(text): isbn_pattern = r'(?:ISBN(?:-1[03])?:?\s*)?(?=[0-9X]{10}$|(?=(?:[0-9]+[-\s]){3})[-\s0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[-\s]){4})[-\s0-9]{17}$)(?:97[89][-\s]?)?[0-9]{1,5}[-\s]?[0-9]+[-\s]?[0-9]+[-\s]?[0-9X]' isbns = re.findall(isbn_pattern, text) valid_isbns = [] for isbn in isbns: # Menghapus karakter non-digit dan 'X' clean_isbn = re.sub(r'[^0-9X]', '', isbn) if len(clean_isbn) == 10: valid_isbns.append(validate_isbn10(clean_isbn)) elif len(clean_isbn) == 13: valid_isbns.append(validate_isbn13(clean_isbn)) return valid_isbns def validate_isbn10(isbn): if len(isbn) != 10: return False total = sum((10 - i) * (9 if x == 'X' else int(x)) for i, x in enumerate(isbn)) return total % 11 == 0 def validate_isbn13(isbn): if len(isbn) != 13: return False total = sum((3 if i % 2 else 1) * int(x) for i, x in enumerate(isbn)) return total % 10 == 0 # Contoh penggunaan text = """ Beberapa contoh ISBN: ISBN-10: 0-306-40615-2 ISBN-13: 978-0-306-40615-7 Invalid: 1234567890 """ valid_isbns = extract_and_validate_isbn(text) print("Valid ISBNs:", valid_isbns)
Penjelasan:
- Pattern ISBN mencocokkan berbagai format ISBN-10 dan ISBN-13, termasuk dengan atau tanpa tanda hubung dan spasi.
validate_isbn10
danvalidate_isbn13
mengimplementasikan algoritma pengecekan digit untuk masing-masing format ISBN.
- Named Groups dan Backreferences:
pythonimport re def parse_html_tag(html): pattern = r'<(?P<tag>[a-zA-Z0-9]+)(?P<attrs>\s+[^>]*)?>(?P<content>.*?)</(?P=tag)>' match = re.search(pattern, html, re.DOTALL) if match: return { 'tag': match.group('tag'), 'attributes': match.group('attrs').strip(), 'content': match.group('content') } return None # Contoh penggunaan html_snippet = '<div class="container">This is some content</div>' result = parse_html_tag(html_snippet) print(result)
Penjelasan:
(?P<name>...)
digunakan untuk membuat named capture groups.(?P=tag)
adalah backreference ke named group 'tag', memastikan tag penutup cocok dengan tag pembuka.re.DOTALL
memungkinkan.
untuk mencocokkan newline.
Teknik-teknik lanjutan ini mendemonstrasikan kekuatan dan fleksibilitas RegEx dalam menangani berbagai tugas pemrosesan teks yang kompleks. Namun, penting untuk diingat bahwa RegEx yang sangat kompleks dapat sulit dibaca dan di-maintain. Dalam beberapa kasus, mungkin lebih baik untuk memecah pola yang kompleks menjadi beberapa langkah atau menggunakan parser khusus untuk tugas-tugas tertentu seperti parsing HTML atau XML.
Tambahan example :
- Penggunaan Lookahead dan Lookbehind:
Lookahead dan lookbehind adalah teknik yang powerful untuk mencocokkan pola berdasarkan konteks tanpa mengkonsumsi karakter dalam string.
pythonimport re def validate_password_complex(password): # Password harus memiliki 8-20 karakter, setidaknya satu huruf besar, # satu huruf kecil, satu angka, satu karakter khusus, dan tidak boleh # diawali atau diakhiri dengan karakter khusus. pattern = r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])(?!.*[@$!%*?&]$)(?!^[@$!%*?&])[\w@$!%*?&]{8,20}$' return bool(re.match(pattern, password)) # Contoh penggunaan passwords = [ "Abc123!@#", "Weak", "NoSpecialChar123", "@StartWithSpecial123", "EndWithSpecial123@", "Valid1Password!", "TooLongPassword1234567!" ] for password in passwords: print(f"'{password}' is valid: {validate_password_complex(password)}")
Penjelasan:
(?=.*[A-Z])
adalah positive lookahead untuk huruf besar(?=.*[a-z])
adalah positive lookahead untuk huruf kecil(?=.*\d)
adalah positive lookahead untuk angka(?=.*[@$!%*?&])
adalah positive lookahead untuk karakter khusus(?!.*[@$!%*?&]$)
adalah negative lookahead untuk memastikan tidak diakhiri karakter khusus(?!^[@$!%*?&])
adalah negative lookahead untuk memastikan tidak diawali karakter khusus
- Penggunaan Conditional Patterns:
RegEx memungkinkan penggunaan pola bersyarat berdasarkan kecocokan sebelumnya.
pythonimport re def parse_date(date_string): pattern = r'^(?:(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})|(?P<day2>\d{2})/(?P<month2>\d{2})/(?P<year2>\d{4}))$' match = re.match(pattern, date_string) if match: if match.group('year'): return { 'format': 'YYYY-MM-DD', 'year': match.group('year'), 'month': match.group('month'), 'day': match.group('day') } else: return { 'format': 'DD/MM/YYYY', 'year': match.group('year2'), 'month': match.group('month2'), 'day': match.group('day2') } return None # Contoh penggunaan dates = ['2024-03-15', '15/03/2024', '2024/03/15'] for date in dates: result = parse_date(date) print(f"Date: {date}, Parsed: {result}")
Penjelasan:
- Pattern ini menggunakan conditional matching untuk mencocokkan dua format tanggal yang berbeda.
(?:...)
digunakan untuk non-capturing groups.- Named groups digunakan untuk mengekstrak komponen tanggal.
- Recursive Patterns:
Python's re modul tidak mendukung recursive patterns secara langsung, tetapi kita bisa mensimulasikannya untuk kasus-kasus tertentu.
pythonimport re def match_nested_parentheses(text): pattern = r'\((?:[^()]|\((?:[^()]|\((?:[^()]|\([^()]*\))*\))*\))*\)' return re.findall(pattern, text) # Contoh penggunaan text = "This (is (a)) test (with (nested (parentheses)))" matches = match_nested_parentheses(text) print("Matched nested parentheses:", matches)
Penjelasan:
- Pattern ini dapat mencocokkan tanda kurung bersarang hingga 3 level.
- Untuk mencocokkan level yang lebih dalam, Anda perlu memperpanjang pola atau menggunakan pendekatan iteratif.
- Penggunaan RegEx dengan Pandas untuk Data Cleaning:
pythonimport pandas as pd import re # Membuat DataFrame contoh df = pd.DataFrame({ 'Name': ['John Doe', 'Jane Smith', 'Bob Johnson'], 'Email': ['john.doe@example.com', 'jane.smith@company.co.uk', 'bob123@gmail.com'], 'Phone': ['(123) 456-7890', '+1-987-654-3210', '555.123.4567'] }) # Fungsi untuk menormalisasi nomor telepon def normalize_phone(phone): # Menghapus semua karakter non-digit digits_only = re.sub(r'\D', '', phone) # Format menjadi XXX-XXX-XXXX return f"{digits_only[:3]}-{digits_only[3:6]}-{digits_only[6:]}" # Aplikasikan normalisasi ke kolom 'Phone' df['Normalized Phone'] = df['Phone'].apply(normalize_phone) # Ekstrak domain email df['Email Domain'] = df['Email'].str.extract(r'@(.+)$') print(df)
Penjelasan:
re.sub(r'\D', '', phone)
menghapus semua karakter non-digit dari nomor telepon..str.extract(r'@(.+)$')
mengekstrak domain email menggunakan metode pandasstr.extract()
.
- Penggunaan Verbose RegEx:
Untuk pola yang sangat kompleks, penggunaan mode verbose dapat meningkatkan keterbacaan.
pythonimport re credit_card_pattern = re.compile(r''' ^(?: (?P<visa>4[0-9]{12}(?:[0-9]{3})?) | (?P<mastercard>5[1-5][0-9]{14}) | (?P<amex>3[47][0-9]{13}) | (?P<discover>6(?:011|5[0-9]{2})[0-9]{12}) )$ ''', re.VERBOSE) def validate_credit_card(number): number = re.sub(r'\D', '', number) # Remove non-digits match = credit_card_pattern.match(number) if match: return match.lastgroup return None # Contoh penggunaan cards = [ '4111111111111111', # Visa '5555555555554444', # Mastercard '378282246310005', # American Express '6011111111111117', # Discover '1234567890123456' # Invalid ] for card in cards: result = validate_credit_card(card) print(f"Card: {card}, Type: {result}")
Penjelasan:
re.VERBOSE
memungkinkan kita menulis pola RegEx yang lebih mudah dibaca dengan komentar dan pemformatan.- Pattern ini mencocokkan dan mengidentifikasi berbagai jenis kartu kredit.
match.lastgroup
mengembalikan nama grup yang cocok, yang sesuai dengan jenis kartu kredit.
Contoh-contoh ini mendemonstrasikan beberapa teknik lanjutan dalam penggunaan RegEx di Python. RegEx adalah alat yang sangat kuat untuk pemrosesan teks, tetapi perlu digunakan dengan hati-hati. Pola yang terlalu kompleks dapat sulit dipelihara dan mungkin memiliki implikasi performa. Selalu pertimbangkan trade-off antara kompleksitas RegEx dan kejelasan kode.
Tambahan contoh implentasi :
- Parsing Log dengan Format Kompleks:
Misalkan kita memiliki log dengan format yang kompleks dan ingin mengekstrak informasi spesifik dari dalamnya.
pythonimport re from datetime import datetime log_pattern = re.compile(r''' (?P<timestamp>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2},\d{3})\s+ # Timestamp \[(?P<thread>[^\]]+)\]\s+ # Thread (?P<level>\w+)\s+ # Log level (?P<logger>[^\s]+)\s+-\s+ # Logger name (?P<message>.*?) # Log message (?:\s+\((?P<source_file>[^:]+):(?P<line_number>\d+)\))?$ # Source file and line number (optional) ''', re.VERBOSE) def parse_log_line(line): match = log_pattern.match(line) if match: data = match.groupdict() data['timestamp'] = datetime.strptime(data['timestamp'], '%Y-%m-%d %H:%M:%S,%f') data['line_number'] = int(data['line_number']) if data['line_number'] else None return data return None # Contoh penggunaan log_lines = [ "2024-03-15 10:23:45,123 [main] INFO com.example.App - Application started (App.java:42)", "2024-03-15 10:23:46,234 [worker-1] WARN com.example.Service - Connection timeout", "2024-03-15 10:23:47,345 [worker-2] ERROR com.example.Database - Query failed: SELECT * FROM users" ] for line in log_lines: parsed = parse_log_line(line) if parsed: print(f"Timestamp: {parsed['timestamp']}") print(f"Thread: {parsed['thread']}") print(f"Level: {parsed['level']}") print(f"Logger: {parsed['logger']}") print(f"Message: {parsed['message']}") print(f"Source: {parsed['source_file']}:{parsed['line_number']}") print("---")
- Validasi dan Normalisasi Alamat Email Kompleks:
pythonimport re email_pattern = re.compile(r''' (?P<local_part> (?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*) | (?:"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*") ) @ (?P<domain> (?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?) | (?:\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3} (?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-zA-Z0-9-]*[a-zA-Z0-9]: (?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+) \]) ) ''', re.VERBOSE) def validate_and_normalize_email(email): match = email_pattern.match(email) if match: local_part = match.group('local_part') domain = match.group('domain') # Normalize local part (remove quotes if unnecessary) if local_part.startswith('"') and local_part.endswith('"'): unquoted = local_part[1:-1] if email_pattern.match(f"{unquoted}@{domain}"): local_part = unquoted # Normalize domain to lowercase domain = domain.lower() return f"{local_part}@{domain}" return None # Contoh penggunaan emails = [ "user@example.com", "user+tag@example.com", '"very.unusual.@.unusual.com"@example.com', "admin@localhost", "user@[IPv6:2001:db8::1]", "invalid@email@example.com" ] for email in emails: normalized = validate_and_normalize_email(email) print(f"Original: {email}") print(f"Normalized: {normalized}") print("---")
- Parsing dan Validasi Expresi Matematika:
pythonimport re class ExpressionParser: def __init__(self): self.tokens = [] self.token_pattern = re.compile(r''' (?P<number>\d+(?:\.\d*)?) | (?P<operator>[+\-*/^]) | (?P<parenthesis>[\(\)]) ''', re.VERBOSE) def tokenize(self, expression): self.tokens = [] for match in self.token_pattern.finditer(expression): token = match.groupdict() token_type = next(k for k, v in token.items() if v is not None) self.tokens.append((token_type, match.group())) return self.tokens def validate(self): parenthesis_count = 0 last_token_type = None for token_type, value in self.tokens: if token_type == 'parenthesis': if value == '(': parenthesis_count += 1 else: parenthesis_count -= 1 if parenthesis_count < 0: return False if token_type == 'operator': if last_token_type in ['operator', None]: return False elif token_type == 'number': if last_token_type == 'number': return False last_token_type = token_type return parenthesis_count == 0 and last_token_type != 'operator' # Contoh penggunaan parser = ExpressionParser() expressions = [ "3 + 4 * 2 / ( 1 - 5 ) ^ 2", "3.14 + 2.0 * (7 - 3)", "1 + + 2", "((2 + 3) * 4", "5 +" ] for expr in expressions: tokens = parser.tokenize(expr) is_valid = parser.validate() print(f"Expression: {expr}") print(f"Tokens: {tokens}") print(f"Valid: {is_valid}") print("---")
- Ekstraksi dan Analisis Struktur HTML Kompleks:
pythonimport re from collections import defaultdict class HTMLAnalyzer: def __init__(self): self.tag_pattern = re.compile(r'<(?P<tag>[a-zA-Z0-9]+)(?P<attrs>[^>]*)>(?P<content>.*?)</(?P=tag)>', re.DOTALL) self.attr_pattern = re.compile(r'(?P<attr>[a-zA-Z0-9-]+)=["\'](.*?)["\']') def analyze(self, html): tags = defaultdict(int) attributes = defaultdict(lambda: defaultdict(int)) for match in self.tag_pattern.finditer(html): tag = match.group('tag') attrs = match.group('attrs') content = match.group('content') tags[tag] += 1 for attr_match in self.attr_pattern.finditer(attrs): attr = attr_match.group('attr') attributes[tag][attr] += 1 # Recursive analysis for nested content nested_tags, nested_attrs = self.analyze(content) for nested_tag, count in nested_tags.items(): tags[nested_tag] += count for nested_tag, nested_attr_dict in nested_attrs.items(): for nested_attr, nested_count in nested_attr_dict.items(): attributes[nested_tag][nested_attr] += nested_count return dict(tags), dict(attributes) # Contoh penggunaan html_content = """ <html> <head> <title>Example Page</title> <meta charset="UTF-8"> </head> <body> <div class="container"> <h1 id="main-title">Welcome</h1> <p>This is a <span class="highlight">sample</span> paragraph.</p> <ul> <li>Item 1</li> <li>Item 2</li> </ul> </div> </body> </html> """ analyzer = HTMLAnalyzer() tag_count, attribute_usage = analyzer.analyze(html_content) print("Tag Count:") for tag, count in tag_count.items(): print(f" {tag}: {count}") print("\nAttribute Usage:") for tag, attrs in attribute_usage.items(): print(f" {tag}:") for attr, count in attrs.items(): print(f" {attr}: {count}")
Contoh-contoh ini mendemonstrasikan penggunaan RegEx yang lebih kompleks dan canggih dalam Python untuk menangani berbagai tugas pemrosesan teks yang rumit. Mereka mencakup:
- Parsing log dengan format yang kompleks
- Validasi dan normalisasi alamat email sesuai dengan RFC 5322
- Parsing dan validasi ekspresi matematika
- Analisis struktur HTML yang kompleks
Perlu diingat bahwa meskipun RegEx sangat kuat, untuk beberapa tugas yang sangat kompleks (seperti parsing HTML atau XML yang lengkap), mungkin lebih baik menggunakan parser khusus atau library yang dirancang untuk tugas tersebut. RegEx sebaiknya digunakan dengan bijak, mempertimbangkan trade-off antara kompleksitas, kinerja, dan kemudahan pemeliharaan.
Comments
Post a Comment