Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Mesin ekspresi reguler di .NET adalah alat canggih berfitur lengkap yang memproses teks berdasarkan kecocokan pola daripada membandingkan dan mencocokkan teks literal. Dalam kebanyakan kasus, ia melakukan pencocokan pola dengan cepat dan efisien. Namun, dalam beberapa kasus, mesin ekspresi reguler dapat tampak lambat. Dalam kasus ekstrem, bahkan dapat tampak berhenti merespons karena memproses input yang relatif kecil selama berjam-jam atau bahkan hari.
Artikel ini menguraikan beberapa praktik terbaik yang dapat diadopsi pengembang untuk memastikan bahwa ekspresi reguler mereka mencapai performa optimal.
Peringatan
Saat menggunakan System.Text.RegularExpressions untuk memproses masukan yang tidak tepercaya, berikan batas waktu. Pengguna berbahaya dapat memberikan input ke RegularExpressions, menyebabkan serangan Penolakan Layanan . API kerangka kerja ASP.NET Core yang menggunakan RegularExpressions meneruskan batas waktu.
Menggunakan pola tepercaya
Mesin ekspresi reguler .NET dirancang dengan asumsi bahwa pola tepercaya, yaitu, mereka ditulis atau ditinjau oleh pengembang aplikasi, tidak disediakan oleh pengguna akhir atau sumber lain yang tidak tepercaya. Pola dapat menyebabkan konsumsi sumber daya yang berlebihan terlepas dari teks input, dan mesin ekspresi reguler tidak mencoba menjaga terhadap pola yang bermusuhan.
Jika aplikasi Anda perlu menerima ekspresi pencarian dari pengguna, hindari meneruskan input pengguna secara langsung sebagai pola regex. Sebagai gantinya, pertimbangkan alternatif ini:
- Mendukung sintaks pencarian terbatas (seperti wildcard sederhana atau pencocokan substring) yang Anda terjemahkan ke dalam pola regex secara internal.
- Gunakan Regex.Escape untuk memperlakukan teks yang disediakan pengguna sebagai string harfiah dalam pola.
Fitur seperti nilai waktu habis dan RegexOptions.NonBacktracking (yang menjamin pemrosesan linear-time dalam panjang input) membantu melindungi dari masalah performa yang tidak disengaja dalam pola yang ditulis pengembang. Mereka tidak dimaksudkan sebagai batas keamanan terhadap pola berbahaya.
Pertimbangkan sumber input
Secara umum, ekspresi reguler dapat menerima dua jenis input: dibatasi atau tidak dibatasi. Input yang dibatasi adalah teks yang berasal dari sumber yang diketahui atau dapat diandalkan dan mengikuti format yang telah ditentukan sebelumnya. Input yang tidak dibatasi adalah teks yang berasal dari sumber yang tidak dapat diandalkan, seperti pengguna web, dan mungkin tidak mengikuti format yang telah ditentukan atau diharapkan.
Pola ekspresi reguler sering ditulis agar sesuai dengan input yang valid. Artinya, pengembang memeriksa teks yang ingin mereka cocokkan dan kemudian menulis pola ekspresi reguler yang cocok dengannya. Pengembang kemudian menentukan apakah pola ini memerlukan koreksi atau elaborasi lebih lanjut dengan mengujinya dengan beberapa item input yang valid. Ketika pola cocok dengan semua input yang diduga valid, pola dinyatakan siap produksi, dan dapat disertakan dalam aplikasi yang dirilis. Pendekatan ini membuat pola ekspresi reguler cocok untuk mencocokkan input yang dibatasi. Namun, itu tidak membuatnya cocok untuk mencocokkan input yang tidak dibatasi.
Untuk mencocokkan input yang tidak dibatasi, ekspresi reguler harus menangani tiga jenis teks secara efisien:
- Teks yang cocok dengan pola ekspresi reguler.
- Teks yang tidak cocok dengan pola ekspresi reguler.
- Teks yang hampir cocok dengan pola ekspresi reguler.
Jenis teks terakhir sangat bermasalah untuk ekspresi reguler yang telah ditulis untuk menangani input yang dibatasi. Jika ekspresi reguler tersebut juga bergantung pada backtracking yang luas, mesin ekspresi reguler dapat menghabiskan jumlah waktu yang tidak biasa (dalam beberapa kasus, berjam-jam atau hari) memproses teks yang tampaknya tidak berbahaya.
Peringatan
Contoh berikut menggunakan ekspresi reguler yang rentan terhadap backtracking yang berlebihan dan kemungkinan akan menolak alamat email yang valid. Anda tidak boleh menggunakannya dalam rutinitas validasi email. Jika Anda ingin ekspresi reguler yang memvalidasi alamat email, lihat Cara: Memverifikasi bahwa String Dalam Format Email yang Valid.
Misalnya, pertimbangkan ekspresi reguler yang umum digunakan tetapi bermasalah untuk memvalidasi alias alamat email. Ekspresi ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ reguler ditulis untuk memproses apa yang dianggap sebagai alamat email yang valid. Alamat email yang valid terdiri dari karakter alfanumerik, diikuti oleh nol atau lebih karakter yang dapat berupa alfanumerik, titik, atau tanda hubung. Ekspresi reguler harus diakhir dengan karakter alfanumerik. Namun, seperti yang ditunjukkan oleh contoh berikut, meskipun ekspresi reguler ini menangani input yang valid dengan mudah, performanya tidak efisien ketika memproses input yang hampir valid:
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
public class DesignExample
{
public static void Main()
{
Stopwatch sw;
string[] addresses = { "AAAAAAAAAAA@contoso.com",
"AAAAAAAAAAaaaaaaaaaa!@contoso.com" };
// The following regular expression should not actually be used to
// validate an email address.
string pattern = @"^[0-9A-Z]([-.\w]*[0-9A-Z])*$";
string input;
foreach (var address in addresses)
{
string mailBox = address.Substring(0, address.IndexOf("@"));
int index = 0;
for (int ctr = mailBox.Length - 1; ctr >= 0; ctr--)
{
index++;
input = mailBox.Substring(ctr, index);
sw = Stopwatch.StartNew();
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
sw.Stop();
if (m.Success)
Console.WriteLine("{0,2}. Matched '{1,25}' in {2}",
index, m.Value, sw.Elapsed);
else
Console.WriteLine("{0,2}. Failed '{1,25}' in {2}",
index, input, sw.Elapsed);
}
Console.WriteLine();
}
}
}
// The example displays output similar to the following:
// 1. Matched ' A' in 00:00:00.0007122
// 2. Matched ' AA' in 00:00:00.0000282
// 3. Matched ' AAA' in 00:00:00.0000042
// 4. Matched ' AAAA' in 00:00:00.0000038
// 5. Matched ' AAAAA' in 00:00:00.0000042
// 6. Matched ' AAAAAA' in 00:00:00.0000042
// 7. Matched ' AAAAAAA' in 00:00:00.0000042
// 8. Matched ' AAAAAAAA' in 00:00:00.0000087
// 9. Matched ' AAAAAAAAA' in 00:00:00.0000045
// 10. Matched ' AAAAAAAAAA' in 00:00:00.0000045
// 11. Matched ' AAAAAAAAAAA' in 00:00:00.0000045
//
// 1. Failed ' !' in 00:00:00.0000447
// 2. Failed ' a!' in 00:00:00.0000071
// 3. Failed ' aa!' in 00:00:00.0000071
// 4. Failed ' aaa!' in 00:00:00.0000061
// 5. Failed ' aaaa!' in 00:00:00.0000081
// 6. Failed ' aaaaa!' in 00:00:00.0000126
// 7. Failed ' aaaaaa!' in 00:00:00.0000359
// 8. Failed ' aaaaaaa!' in 00:00:00.0000414
// 9. Failed ' aaaaaaaa!' in 00:00:00.0000758
// 10. Failed ' aaaaaaaaa!' in 00:00:00.0001462
// 11. Failed ' aaaaaaaaaa!' in 00:00:00.0002885
// 12. Failed ' Aaaaaaaaaaa!' in 00:00:00.0005780
// 13. Failed ' AAaaaaaaaaaa!' in 00:00:00.0011628
// 14. Failed ' AAAaaaaaaaaaa!' in 00:00:00.0022851
// 15. Failed ' AAAAaaaaaaaaaa!' in 00:00:00.0045864
// 16. Failed ' AAAAAaaaaaaaaaa!' in 00:00:00.0093168
// 17. Failed ' AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
// 18. Failed ' AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
// 19. Failed ' AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
// 20. Failed ' AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
// 21. Failed ' AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim sw As Stopwatch
Dim addresses() As String = {"AAAAAAAAAAA@contoso.com",
"AAAAAAAAAAaaaaaaaaaa!@contoso.com"}
' The following regular expression should not actually be used to
' validate an email address.
Dim pattern As String = "^[0-9A-Z]([-.\w]*[0-9A-Z])*$"
Dim input As String
For Each address In addresses
Dim mailBox As String = address.Substring(0, address.IndexOf("@"))
Dim index As Integer = 0
For ctr As Integer = mailBox.Length - 1 To 0 Step -1
index += 1
input = mailBox.Substring(ctr, index)
sw = Stopwatch.StartNew()
Dim m As Match = Regex.Match(input, pattern, RegexOptions.IgnoreCase)
sw.Stop()
if m.Success Then
Console.WriteLine("{0,2}. Matched '{1,25}' in {2}",
index, m.Value, sw.Elapsed)
Else
Console.WriteLine("{0,2}. Failed '{1,25}' in {2}",
index, input, sw.Elapsed)
End If
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays output similar to the following:
' 1. Matched ' A' in 00:00:00.0007122
' 2. Matched ' AA' in 00:00:00.0000282
' 3. Matched ' AAA' in 00:00:00.0000042
' 4. Matched ' AAAA' in 00:00:00.0000038
' 5. Matched ' AAAAA' in 00:00:00.0000042
' 6. Matched ' AAAAAA' in 00:00:00.0000042
' 7. Matched ' AAAAAAA' in 00:00:00.0000042
' 8. Matched ' AAAAAAAA' in 00:00:00.0000087
' 9. Matched ' AAAAAAAAA' in 00:00:00.0000045
' 10. Matched ' AAAAAAAAAA' in 00:00:00.0000045
' 11. Matched ' AAAAAAAAAAA' in 00:00:00.0000045
'
' 1. Failed ' !' in 00:00:00.0000447
' 2. Failed ' a!' in 00:00:00.0000071
' 3. Failed ' aa!' in 00:00:00.0000071
' 4. Failed ' aaa!' in 00:00:00.0000061
' 5. Failed ' aaaa!' in 00:00:00.0000081
' 6. Failed ' aaaaa!' in 00:00:00.0000126
' 7. Failed ' aaaaaa!' in 00:00:00.0000359
' 8. Failed ' aaaaaaa!' in 00:00:00.0000414
' 9. Failed ' aaaaaaaa!' in 00:00:00.0000758
' 10. Failed ' aaaaaaaaa!' in 00:00:00.0001462
' 11. Failed ' aaaaaaaaaa!' in 00:00:00.0002885
' 12. Failed ' Aaaaaaaaaaa!' in 00:00:00.0005780
' 13. Failed ' AAaaaaaaaaaa!' in 00:00:00.0011628
' 14. Failed ' AAAaaaaaaaaaa!' in 00:00:00.0022851
' 15. Failed ' AAAAaaaaaaaaaa!' in 00:00:00.0045864
' 16. Failed ' AAAAAaaaaaaaaaa!' in 00:00:00.0093168
' 17. Failed ' AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
' 18. Failed ' AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
' 19. Failed ' AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
' 20. Failed ' AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
' 21. Failed ' AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372
Seperti yang ditunjukkan oleh output dari contoh sebelumnya, mesin ekspresi reguler memproses alias email yang valid dalam interval waktu yang hampir sama terlepas dari panjangnya. Di sisi lain, ketika alamat email yang hampir valid memiliki lebih dari lima karakter, waktu pemrosesan sekitar dua kali lipat untuk setiap karakter tambahan dalam string. Oleh karena itu, string 28 karakter yang hampir valid akan memakan waktu lebih dari satu jam untuk diproses, dan string 33 karakter yang hampir valid akan memakan waktu hampir satu hari untuk diproses.
Karena ekspresi reguler ini dikembangkan hanya dengan mempertimbangkan format input yang akan dicocokkan, ekspresi reguler ini gagal memperhitungkan input yang tidak cocok dengan pola. Pengawasan ini, pada gilirannya, dapat memungkinkan input yang tidak dibatasi yang hampir cocok dengan pola ekspresi reguler untuk menurunkan performa secara signifikan.
Untuk mengatasi masalah ini, Anda dapat melakukan hal berikut:
Saat mengembangkan pola, Anda harus mempertimbangkan bagaimana backtracking dapat memengaruhi performa mesin ekspresi reguler, terutama jika ekspresi reguler Anda dirancang untuk memproses input yang tidak dibatasi. Untuk informasi selengkapnya, lihat bagian Mengambil Kontrol atas Backtracking.
Uji ekspresi reguler Anda secara menyeluruh menggunakan input yang tidak valid, nyaris valid, dan valid. Anda dapat menggunakan Rex untuk menghasilkan input secara acak untuk ekspresi reguler tertentu. Rex adalah alat eksplorasi ekspresi reguler dari Microsoft Research.
Menangani instansiasi objek dengan tepat
Inti dari model objek ekspresi reguler .NET adalah kelas System.Text.RegularExpressions.Regex, yang mewakili mesin ekspresi reguler. Seringkali, faktor terbesar tunggal yang memengaruhi performa ekspresi reguler adalah cara Regex mesin digunakan. Menentukan ekspresi reguler melibatkan penggabungan erat mesin ekspresi reguler dengan pola ekspresi reguler. Proses penghubungan itu mahal, baik itu melibatkan pembuatan instans objek Regex dengan meneruskan pola ekspresi reguler ke konstruktornya, atau memanggil metode statis dengan meneruskan pola ekspresi reguler dan string yang akan dianalisis.
Nota
Untuk diskusi terperinci tentang implikasi performa penggunaan ekspresi reguler yang ditafsirkan dan dikompilasi, lihat posting blog Optimalisasi Performa Ekspresi Reguler, Bagian II: Mengendalikan Backtracking.
Anda dapat menggabungkan mesin ekspresi reguler dengan pola ekspresi reguler tertentu lalu menggunakan mesin untuk mencocokkan teks dengan beberapa cara:
Anda dapat memanggil metode pencocokan pola statis, seperti Regex.Match(String, String). Metode ini tidak memerlukan pembuatan objek regex.
Anda dapat membuat instans objek Regex dan memanggil metode pencocokan pola instans dari ekspresi reguler yang ditafsirkan. Ini adalah metode default untuk menghubungkan mesin ekspresi reguler ke pola ekspresi reguler. Ini terjadi ketika sebuah Regex objek dibuat tanpa argumen
optionsyang menyertakan tanda Compiled.Anda dapat menciptakan objek Regex dan menggunakan metode pencocokan pola instance dari ekspresi reguler yang dihasilkan oleh sumber. Teknik ini direkomendasikan dalam banyak kasus. Untuk melakukannya, tempatkan atribut GeneratedRegexAttribute pada sebuah metode parsial yang mengembalikan
Regex.Anda dapat membuat objek instans Regex dan memanggil metode pencocokan pola instansi dari ekspresi reguler yang dikompilasi. Objek ekspresi reguler mewakili pola yang telah dikompilasi saat Regex objek diinstansiasi dengan argumen
optionsyang menyertakan penanda Compiled.
Cara khusus di mana Anda memanggil metode pencocokan ekspresi reguler dapat memengaruhi performa aplikasi Anda. Bagian berikut membahas kapan menggunakan panggilan metode statis, ekspresi reguler yang dihasilkan sumber, ekspresi reguler yang ditafsirkan, dan ekspresi reguler yang dikompilasi untuk meningkatkan performa aplikasi Anda.
Penting
Bentuk panggilan metode (statis, ditafsirkan, dihasilkan sumber, dikompilasi) memengaruhi performa jika ekspresi reguler yang sama digunakan berulang kali dalam panggilan metode, atau jika aplikasi menggunakan objek ekspresi reguler secara ekstensif.
Ekspresi reguler statis
Metode ekspresi reguler statis direkomendasikan sebagai alternatif untuk berulang kali membuat instans objek ekspresi reguler dengan ekspresi reguler yang sama. Tidak seperti pola ekspresi reguler yang digunakan oleh objek ekspresi reguler, kode operasi (opcode) atau bahasa perantara umum (CIL) yang dikompilasi dari pola yang digunakan dalam panggilan metode statis di-cache secara internal oleh mesin ekspresi reguler.
Misalnya, penanganan aktivitas sering memanggil metode lain untuk memvalidasi input pengguna. Contoh ini tercermin dalam kode berikut, di mana Button peristiwa kontrol Click digunakan untuk memanggil metode bernama IsValidCurrency, yang memeriksa apakah pengguna telah memasukkan simbol mata uang diikuti oleh setidaknya satu digit desimal.
public void OKButton_Click(object sender, EventArgs e)
{
if (! String.IsNullOrEmpty(sourceCurrency.Text))
if (RegexLib.IsValidCurrency(sourceCurrency.Text))
PerformConversion();
else
status.Text = "The source currency value is invalid.";
}
Public Sub OKButton_Click(sender As Object, e As EventArgs) _
Handles OKButton.Click
If Not String.IsNullOrEmpty(sourceCurrency.Text) Then
If RegexLib.IsValidCurrency(sourceCurrency.Text) Then
PerformConversion()
Else
status.Text = "The source currency value is invalid."
End If
End If
End Sub
Implementasi metode IsValidCurrency yang tidak efisien ditunjukkan dalam contoh berikut:
Nota
Setiap kali metode dipanggil, menghasilkan ulang objek Regex menggunakan pola yang sama. Ini, pada gilirannya, berarti bahwa pola ekspresi reguler harus dikompresi ulang setiap kali metode dipanggil.
using System;
using System.Text.RegularExpressions;
public class RegexLib
{
public static bool IsValidCurrency(string currencyValue)
{
string pattern = @"\p{Sc}+\s*\d+";
Regex currencyRegex = new Regex(pattern);
return currencyRegex.IsMatch(currencyValue);
}
}
Imports System.Text.RegularExpressions
Public Module RegexLib
Public Function IsValidCurrency(currencyValue As String) As Boolean
Dim pattern As String = "\p{Sc}+\s*\d+"
Dim currencyRegex As New Regex(pattern)
Return currencyRegex.IsMatch(currencyValue)
End Function
End Module
Anda harus mengganti kode tidak efisien sebelumnya dengan panggilan ke metode statis Regex.IsMatch(String, String) . Pendekatan ini menghilangkan kebutuhan untuk menginisialisasi objek Regex setiap kali Anda ingin memanggil metode pencocokan pola, dan memungkinkan mesin pencocokan pola untuk mengambil versi ekspresi reguler yang sudah dikompilasi dari cache-nya.
using System;
using System.Text.RegularExpressions;
public class RegexLib2
{
public static bool IsValidCurrency(string currencyValue)
{
string pattern = @"\p{Sc}+\s*\d+";
return Regex.IsMatch(currencyValue, pattern);
}
}
Imports System.Text.RegularExpressions
Public Module RegexLib
Public Function IsValidCurrency(currencyValue As String) As Boolean
Dim pattern As String = "\p{Sc}+\s*\d+"
Return Regex.IsMatch(currencyValue, pattern)
End Function
End Module
Secara default, 15 pola ekspresi reguler statis terakhir yang terakhir digunakan di-cache. Untuk aplikasi yang memerlukan lebih banyak ekspresi reguler statis yang di-cache, ukuran cache dapat disesuaikan dengan mengatur Regex.CacheSize properti .
Ekspresi \p{Sc}+\s*\d+ reguler yang digunakan dalam contoh ini memverifikasi bahwa string input memiliki simbol mata uang dan setidaknya satu digit desimal. Pola didefinisikan seperti yang ditunjukkan dalam tabel berikut:
| Pola | Deskripsi |
|---|---|
\p{Sc}+ |
Cocok dengan satu atau beberapa karakter dalam kategori Simbol Unicode, Mata Uang. |
\s* |
Cocok dengan nol atau lebih karakter spasi kosong. |
\d+ |
Cocok dengan satu atau beberapa digit desimal. |
Ditafsirkan vs. ekspresi reguler yang dihasilkan sumber vs. dikompilasi
Pola ekspresi reguler yang tidak terikat ke mesin pemrosesan ekspresi reguler melalui spesifikasi opsi Compiled diinterpretasikan. Saat objek ekspresi reguler dibuat, mesin ekspresi reguler mengonversi ekspresi reguler ke sekumpulan kode operasi. Ketika metode instans dipanggil, kode operasi dikonversi ke CIL dan dijalankan oleh pengkompilasi JIT. Demikian pula, ketika metode ekspresi reguler statis dipanggil dan ekspresi reguler tidak dapat ditemukan di cache, mesin ekspresi reguler mengonversi ekspresi reguler ke sekumpulan kode operasi dan menyimpannya di cache. Kemudian mengonversi kode operasi ini ke CIL sehingga pengkompilasi JIT dapat mengeksekusinya. Ekspresi reguler yang ditafsirkan dapat mengurangi waktu startup, namun mengakibatkan waktu eksekusi yang lebih lambat. Karena sifat proses ini, mereka paling baik digunakan ketika ekspresi reguler diterapkan dalam sejumlah kecil panggilan metode, atau jika jumlah pasti panggilan ke metode ekspresi reguler tidak diketahui namun diperkirakan sedikit. Ketika jumlah panggilan metode meningkat, perolehan performa dari pengurangan waktu mulai terkalahkan oleh kecepatan eksekusi yang lebih lambat.
Pola ekspresi reguler yang terikat ke perangkat pemrosesan ekspresi reguler melalui spesifikasi opsi Compiled adalah dikompilasi. Oleh karena itu, ketika objek ekspresi reguler dibuat, atau ketika metode ekspresi reguler statis dipanggil dan ekspresi reguler tidak dapat ditemukan di cache, mesin ekspresi reguler mengonversi ekspresi reguler ke serangkaian kode operasi perantara. Kode-kode ini kemudian dikonversi ke CIL. Ketika metode dipanggil, pengkompilasi JIT menjalankan CIL. Berbeda dengan ekspresi reguler yang ditafsirkan, ekspresi reguler yang dikompilasi meningkatkan waktu startup tetapi menjalankan metode pencocokan pola individual lebih cepat. Akibatnya, manfaat performa yang dihasilkan dari mengkompilasi ekspresi reguler meningkat sebanding dengan jumlah metode ekspresi reguler yang dipanggil.
Pola ekspresi reguler yang terikat ke mesin ekspresi reguler melalui hiasan Regexmetode -returning dengan GeneratedRegexAttribute atribut adalah sumber yang dihasilkan. Generator sumber daya, yang terhubung ke kompilator, menghasilkan implementasi turunan khusus sebagai kode C# dengan logika yang mirip dengan apa yang Regex hasilkan dalam CIL. Anda mendapatkan semua manfaat performa throughput RegexOptions.Compiled (bahkan lebih) dan manfaat start-up dari Regex.CompileToAssembly, tetapi tanpa kompleksitas CompileToAssembly. Sumber yang dipancarkan adalah bagian dari proyek Anda, yang berarti juga mudah dilihat dan dapat di-debug.
Untuk meringkas, kami sarankan Anda:
- Gunakan ekspresi reguler yang telah diinterpretasikan saat Anda relatif jarang memanggil metode ekspresi reguler dengan ekspresi reguler tertentu.
- Gunakan ekspresi reguler yang dihasilkan sumber jika Anda menggunakan
RegexC# dengan argumen yang diketahui pada waktu kompilasi, dan Anda menggunakan ekspresi reguler tertentu yang relatif sering. - Gunakan ekspresi reguler yang dikompilasi saat Anda memanggil metode ekspresi reguler dengan ekspresi reguler tertentu yang relatif sering dan Anda menggunakan .NET 6 atau versi yang lebih lama.
Sulit untuk menentukan ambang batas yang tepat di mana kecepatan eksekusi yang lebih lambat dari ekspresi reguler yang ditafsirkan melebihi keuntungan dari pengurangan waktu startup mereka. Sulit juga untuk menentukan ambang batas di mana waktu mulai yang lebih lambat dari ekspresi reguler yang dihasilkan sumber atau dikompilasi melebihi keuntungan dari kecepatan eksekusi mereka yang lebih cepat. Ambang tergantung pada berbagai faktor, termasuk kompleksitas ekspresi reguler dan data spesifik yang diprosesnya. Untuk menentukan ekspresi reguler mana yang menawarkan performa terbaik untuk skenario aplikasi tertentu, Anda dapat menggunakan Stopwatch kelas untuk membandingkan waktu eksekusinya.
Contoh berikut membandingkan performa ekspresi reguler yang dikompilasi, dihasilkan dari sumber, dan ditafsirkan saat membaca 10 kalimat pertama dan ketika membaca semua kalimat dalam teks Magna Carta karya William D. Guthrie , dan Pidato Lainnya. Seperti yang ditunjukkan oleh output dari contoh, ketika hanya 10 panggilan yang dilakukan untuk metode pencocokan ekspresi reguler, ekspresi reguler yang ditafsirkan atau dihasilkan sumber menawarkan performa yang lebih baik daripada ekspresi reguler yang dikompilasi. Namun, ekspresi reguler yang dikompilasi menawarkan performa yang lebih baik ketika sejumlah besar panggilan (dalam hal ini, lebih dari 13.000) dilakukan.
const string Pattern = @"\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]";
static readonly HttpClient s_client = new();
[GeneratedRegex(Pattern, RegexOptions.Singleline)]
private static partial Regex GeneratedRegex();
public async static Task RunIt()
{
Stopwatch sw;
Match match;
int ctr;
string text =
await s_client.GetStringAsync("https://www.gutenberg.org/cache/epub/64197/pg64197.txt");
// Read first ten sentences with interpreted regex.
Console.WriteLine("10 Sentences with Interpreted Regex:");
sw = Stopwatch.StartNew();
Regex int10 = new(Pattern, RegexOptions.Singleline);
match = int10.Match(text);
for (ctr = 0; ctr <= 9; ctr++)
{
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine($" {ctr} matches in {sw.Elapsed}");
// Read first ten sentences with compiled regex.
Console.WriteLine("10 Sentences with Compiled Regex:");
sw = Stopwatch.StartNew();
Regex comp10 = new Regex(Pattern,
RegexOptions.Singleline | RegexOptions.Compiled);
match = comp10.Match(text);
for (ctr = 0; ctr <= 9; ctr++)
{
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine($" {ctr} matches in {sw.Elapsed}");
// Read first ten sentences with source-generated regex.
Console.WriteLine("10 Sentences with Source-generated Regex:");
sw = Stopwatch.StartNew();
match = GeneratedRegex().Match(text);
for (ctr = 0; ctr <= 9; ctr++)
{
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine($" {ctr} matches in {sw.Elapsed}");
// Read all sentences with interpreted regex.
Console.WriteLine("All Sentences with Interpreted Regex:");
sw = Stopwatch.StartNew();
Regex intAll = new(Pattern, RegexOptions.Singleline);
match = intAll.Match(text);
int matches = 0;
while (match.Success)
{
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine($" {matches:N0} matches in {sw.Elapsed}");
// Read all sentences with compiled regex.
Console.WriteLine("All Sentences with Compiled Regex:");
sw = Stopwatch.StartNew();
Regex compAll = new(Pattern,
RegexOptions.Singleline | RegexOptions.Compiled);
match = compAll.Match(text);
matches = 0;
while (match.Success)
{
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine($" {matches:N0} matches in {sw.Elapsed}");
// Read all sentences with source-generated regex.
Console.WriteLine("All Sentences with Source-generated Regex:");
sw = Stopwatch.StartNew();
match = GeneratedRegex().Match(text);
matches = 0;
while (match.Success)
{
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine($" {matches:N0} matches in {sw.Elapsed}");
return;
}
/* The example displays output similar to the following:
10 Sentences with Interpreted Regex:
10 matches in 00:00:00.0104920
10 Sentences with Compiled Regex:
10 matches in 00:00:00.0234604
10 Sentences with Source-generated Regex:
10 matches in 00:00:00.0060982
All Sentences with Interpreted Regex:
3,427 matches in 00:00:00.1745455
All Sentences with Compiled Regex:
3,427 matches in 00:00:00.0575488
All Sentences with Source-generated Regex:
3,427 matches in 00:00:00.2698670
*/
Pola ekspresi reguler yang digunakan dalam contoh, \b(\w+((\r?\n)|,?\s))*\w+[.?:;!], didefinisikan seperti yang ditunjukkan dalam tabel berikut:
| Pola | Deskripsi |
|---|---|
\b |
Mulai pencocokan pada batas kata. |
\w+ |
Cocok dengan satu atau lebih karakter penyusun kata. |
(\r?\n)|,?\s) |
Cocok dengan nol atau satu pengembalian pengangkutan diikuti oleh karakter baris baru, atau nol atau satu koma diikuti oleh karakter spasi putih. |
(\w+((\r?\n)|,?\s))* |
Cocok dengan nol atau lebih kemunculan satu atau beberapa karakter kata yang diikuti dengan nol atau satu pengembalian pengangkutan dan karakter garis baru, atau dengan nol atau satu koma diikuti oleh karakter spasi putih. |
\w+ |
Cocok dengan satu atau lebih karakter penyusun kata. |
[.?:;!] |
Cocok dengan titik, tanda tanya, titik dua, titik koma, atau tanda seru. |
Mengelola kembali langkah mundur dengan efektif
Biasanya, mesin ekspresi reguler menggunakan perkembangan linier untuk bergerak melalui string input dan membandingkannya dengan pola ekspresi reguler. Namun, ketika kuantifier yang tidak ditentukan seperti *, , +dan ? digunakan dalam pola ekspresi reguler, mesin ekspresi reguler mungkin menyerahkan sebagian kecocokan parsial yang berhasil dan kembali ke status yang disimpan sebelumnya untuk mencari kecocokan yang berhasil untuk seluruh pola. Proses ini dikenal sebagai pelacakan mundur.
Petunjuk / Saran
Untuk informasi selengkapnya tentang backtracking, lihat Detail perilaku ekspresi reguler dan Backtracking. Untuk pembahasan mendalam tentang backtracking, lihat postingan blog Peningkatan Ekspresi Reguler di .NET 7 dan Mengoptimalkan Performa Ekspresi Reguler.
Dukungan untuk backtracking memberikan daya dan fleksibilitas pada regex. Ini juga menempatkan tanggung jawab untuk mengontrol pengoperasian mesin ekspresi reguler di tangan pengembang ekspresi reguler. Karena pengembang sering tidak menyadari tanggung jawab ini, penyalahgunaan mereka terhadap backtracking atau ketergantungan pada backtracking yang berlebihan sering memainkan peran yang sangat besar dalam menurunkan performa ekspresi reguler. Dalam skenario terburuk, waktu eksekusi dapat dua kali lipat untuk setiap karakter tambahan dalam string input. Bahkan, dengan menggunakan backtracking secara berlebihan, mudah untuk menciptakan yang setara secara programatis dengan perulangan tanpa akhir apabila input hampir cocok dengan pola ekspresi reguler. Mesin ekspresi reguler mungkin membutuhkan waktu berjam-jam atau bahkan berjam-hari untuk memproses string input yang relatif singkat.
Seringkali, aplikasi mengalami penurunan performa akibat penggunaan backtracking meskipun backtracking tidak penting untuk pencocokan pola. Misalnya, ekspresi \b\p{Lu}\w*\b reguler cocok dengan semua kata yang dimulai dengan karakter huruf besar, seperti yang ditunjukkan oleh tabel berikut:
| Pola | Deskripsi |
|---|---|
\b |
Mulai pencocokan pada batas kata. |
\p{Lu} |
Cocok dengan karakter huruf besar. |
\w* |
Cocok dengan nol atau lebih karakter kata. |
\b |
Akhiri pertandingan pada batas kata. |
Karena batas kata tidak sama dengan, atau subset, karakter kata, tidak ada kemungkinan bahwa mesin ekspresi reguler akan melewati batas kata saat mencocokkan karakter kata. Oleh karena itu untuk ekspresi reguler ini, backtracking tidak pernah dapat berkontribusi pada keberhasilan keseluruhan dari kecocokan apa pun. Ini hanya dapat menurunkan performa karena mesin ekspresi reguler dipaksa untuk menyimpan statusnya untuk setiap kecocokan awal yang berhasil dari karakter kata.
Jika Anda menentukan bahwa backtracking tidak diperlukan, Anda dapat menonaktifkannya dengan beberapa cara:
Dengan mengatur opsi RegexOptions.NonBacktracking (yang diperkenalkan di .NET 7). Untuk informasi selengkapnya, lihat mode Nonbacktracking .
Dengan menggunakan elemen bahasa pemrograman
(?>subexpression), yang dikenal sebagai grup atomik. Contoh berikut mengurai string input dengan menggunakan dua ekspresi reguler. Yang pertama,\b\p{Lu}\w*\b, bergantung pada backtracking. Yang kedua,\b\p{Lu}(?>\w*)\b, menonaktifkan backtracking. Seperti yang ditunjukkan oleh output dari contoh, keduanya menghasilkan hasil yang sama:using System; using System.Text.RegularExpressions; public class BackTrack2Example { public static void Main() { string input = "This this word Sentence name Capital"; string pattern = @"\b\p{Lu}\w*\b"; foreach (Match match in Regex.Matches(input, pattern)) Console.WriteLine(match.Value); Console.WriteLine(); pattern = @"\b\p{Lu}(?>\w*)\b"; foreach (Match match in Regex.Matches(input, pattern)) Console.WriteLine(match.Value); } } // The example displays the following output: // This // Sentence // Capital // // This // Sentence // CapitalImports System.Text.RegularExpressions Module Example Public Sub Main() Dim input As String = "This this word Sentence name Capital" Dim pattern As String = "\b\p{Lu}\w*\b" For Each match As Match In Regex.Matches(input, pattern) Console.WriteLine(match.Value) Next Console.WriteLine() pattern = "\b\p{Lu}(?>\w*)\b" For Each match As Match In Regex.Matches(input, pattern) Console.WriteLine(match.Value) Next End Sub End Module ' The example displays the following output: ' This ' Sentence ' Capital ' ' This ' Sentence ' Capital
Dalam banyak kasus, backtracking sangat penting untuk mencocokkan pola ekspresi reguler dengan teks input. Namun, backtracking yang berlebihan dapat sangat menurunkan performa dan menciptakan kesan bahwa aplikasi telah berhenti merespons. Secara khusus, masalah ini muncul ketika kuantor bertumpuk dan teks yang cocok dengan subekspresi luar adalah bagian dari teks yang cocok dengan subekspresi dalam.
Peringatan
Selain menghindari backtracking yang berlebihan, Anda harus menggunakan fitur batas waktu untuk memastikan bahwa backtracking yang berlebihan tidak secara signifikan menurunkan performa regular expression. Untuk informasi selengkapnya, lihat bagian Menggunakan nilai batas waktu .
Misalnya, pola ekspresi reguler ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ dimaksudkan untuk mencocokkan nomor bagian yang terdiri dari setidaknya satu karakter alfanumerik. Setiap karakter tambahan dapat terdiri dari karakter alfanumerik, tanda hubung, garis bawah, atau titik, meskipun karakter terakhir harus alfanumerik. Tanda dolar mengakhiri nomor suku cadang. Dalam beberapa kasus, pola ekspresi reguler ini dapat menunjukkan performa yang buruk karena kuantifier bersarang, dan karena subekspresi [0-9A-Z] adalah subset dari subekspresi [-.\w]*.
Dalam kasus ini, Anda dapat mengoptimalkan performa ekspresi reguler dengan menghapus kuantifier berlapis dan mengganti subekspresi luar dengan lookahead lebar nol atau pernyataan lookbehind. Lookahead dan lookbehind pernyataan adalah jangkar. Mereka tidak memindahkan penunjuk dalam string input tetapi sebaliknya melihat ke depan atau di belakang untuk memeriksa apakah kondisi tertentu terpenuhi. Misalnya, ekspresi reguler nomor bagian dapat ditulis ulang sebagai ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$. Pola ekspresi reguler ini didefinisikan seperti yang ditunjukkan dalam tabel berikut:
| Pola | Deskripsi |
|---|---|
^ |
Mulai kecocokan di awal string input. |
[0-9A-Z] |
Cocokkan karakter alfanumerik. Nomor bagian harus terdiri dari setidaknya karakter ini. |
[-.\w]* |
Cocokkan nol atau lebih kemunculan karakter kata, tanda hubung, atau titik apa pun. |
\$ |
Cocokkan tanda dolar. |
(?<=[0-9A-Z]) |
Lihat di belakang tanda dolar terakhir untuk memastikan bahwa karakter sebelumnya adalah alfanumerik. |
$ |
Akhiri pencocokan di akhir teks input. |
Contoh berikut mengilustrasikan penggunaan ekspresi reguler ini untuk mencocokkan array yang berisi kemungkinan angka bagian:
using System;
using System.Text.RegularExpressions;
public class BackTrack4Example
{
public static void Main()
{
string pattern = @"^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$";
string[] partNos = { "A1C$", "A4", "A4$", "A1603D$", "A1603D#" };
foreach (var input in partNos)
{
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine(match.Value);
else
Console.WriteLine("Match not found.");
}
}
}
// The example displays the following output:
// A1C$
// Match not found.
// A4$
// A1603D$
// Match not found.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$"
Dim partNos() As String = {"A1C$", "A4", "A4$", "A1603D$",
"A1603D#"}
For Each input As String In partNos
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
Console.WriteLine(match.Value)
Else
Console.WriteLine("Match not found.")
End If
Next
End Sub
End Module
' The example displays the following output:
' A1C$
' Match not found.
' A4$
' A1603D$
' Match not found.
Bahasa ekspresi reguler dalam .NET menyertakan elemen bahasa berikut yang dapat Anda gunakan untuk menghilangkan kuantifier berlapis. Untuk informasi selengkapnya, lihat Konstruksi pengelompokan.
| Elemen bahasa | Deskripsi |
|---|---|
(?=
subexpression
)
|
Lookahead positif lebar nol. Melihat ke depan posisi saat ini untuk menentukan apakah subexpression cocok dengan string input. |
(?!
subexpression
)
|
Lookahead negatif lebar nol. Lihat ke depan posisi saat ini untuk menentukan apakah subexpression tidak cocok dengan string input. |
(?<=
subexpression
)
|
Lookbehind positif tanpa lebar. Melihat ke belakang posisi saat ini untuk menentukan apakah subexpression cocok dengan string input. |
(?<!
subexpression
)
|
Negatif lookbehind tanpa lebar. Melihat ke belakang posisi saat ini untuk menentukan apakah subexpression tidak cocok dengan string input. |
Gunakan nilai batas waktu
Jika ekspresi reguler Anda memproses input yang hampir cocok dengan pola ekspresi reguler, sering kali dapat mengandalkan backtracking yang berlebihan, yang berdampak pada performanya secara signifikan. Selain mempertimbangkan dengan cermat penggunaan backtracking dan pengujian ekspresi reguler terhadap input yang hampir cocok, Anda harus selalu menetapkan nilai waktu habis untuk meminimalkan efek backtracking yang berlebihan, jika itu terjadi.
Interval batas waktu ekspresi reguler menentukan periode waktu di mana mesin ekspresi reguler berusaha menemukan satu kecocokan sebelum batas waktu tercapai. Bergantung pada pola ekspresi reguler dan teks masukan, waktu eksekusi mungkin melebihi interval batas waktu yang ditentukan, tetapi mesin tidak akan menghabiskan lebih banyak waktu untuk mundur dibandingkan dengan interval batas waktu tersebut. Interval batas waktu default adalah Regex.InfiniteMatchTimeout, yang berarti bahwa ekspresi reguler tidak akan kehabisan waktu. Anda dapat mengambil alih nilai ini dan menentukan interval waktu habis sebagai berikut:
Panggil konstruktor Regex(String, RegexOptions, TimeSpan) untuk memberikan batas waktu ketika Anda membuat objek Regex.
Panggil metode pencocokan pola statis, seperti Regex.Match(String, String, RegexOptions, TimeSpan) atau Regex.Replace(String, String, String, RegexOptions, TimeSpan), yang menyertakan
matchTimeoutparameter.Atur nilai seluruh proses atau seluruh domain aplikasi dengan kode seperti
AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromMilliseconds(100));.
Jika Anda telah menentukan interval waktu habis dan kecocokan tidak ditemukan di akhir interval tersebut, metode ekspresi reguler akan melemparkan RegexMatchTimeoutException pengecualian. Dalam handler pengecualian, Anda dapat memilih untuk mencoba kembali kecocokan dengan interval durasi tunggu yang lebih lama, membatalkan upaya pencocokan dengan asumsi bahwa tidak ada kecocokan, atau membatalkan upaya pencocokan dan mencatat informasi pengecualian untuk analisis lebih lanjut.
Contoh berikut mendefinisikan metode GetWordData yang menginstansiasi ekspresi reguler dengan interval waktu habis 350 milidetik untuk menghitung jumlah kata dan rata-rata jumlah karakter per kata dalam sebuah dokumen teks. Jika operasi pencocokan melebihi batas waktu, interval batas waktu ditingkatkan sebesar 350 milidetik dan objek Regex diinstansiasi ulang. Jika interval batas waktu baru melebihi satu detik, metode akan melemparkan kembali pengecualian kepada pemanggil.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
public class TimeoutExample
{
public static void Main()
{
RegexUtilities util = new RegexUtilities();
string title = "Doyle - The Hound of the Baskervilles.txt";
try
{
var info = util.GetWordData(title);
Console.WriteLine($"Words: {info.Item1:N0}");
Console.WriteLine($"Average Word Length: {info.Item2:N2} characters");
}
catch (IOException e)
{
Console.WriteLine($"IOException reading file '{title}'");
Console.WriteLine(e.Message);
}
catch (RegexMatchTimeoutException e)
{
Console.WriteLine($"The operation timed out after {e.MatchTimeout.TotalMilliseconds:N0} milliseconds");
}
}
}
public class RegexUtilities
{
public Tuple<int, double> GetWordData(string filename)
{
const int MAX_TIMEOUT = 1000; // Maximum timeout interval in milliseconds.
const int INCREMENT = 350; // Milliseconds increment of timeout.
List<string> exclusions = new List<string>(new string[] { "a", "an", "the" });
int[] wordLengths = new int[29]; // Allocate an array of more than ample size.
string input = null;
StreamReader sr = null;
try
{
sr = new StreamReader(filename);
input = sr.ReadToEnd();
}
catch (FileNotFoundException e)
{
string msg = String.Format("Unable to find the file '{0}'", filename);
throw new IOException(msg, e);
}
catch (IOException e)
{
throw new IOException(e.Message, e);
}
finally
{
if (sr != null) sr.Close();
}
int timeoutInterval = INCREMENT;
bool init = false;
Regex rgx = null;
Match m = null;
int indexPos = 0;
do
{
try
{
if (!init)
{
rgx = new Regex(@"\b\w+\b", RegexOptions.None,
TimeSpan.FromMilliseconds(timeoutInterval));
m = rgx.Match(input, indexPos);
init = true;
}
else
{
m = m.NextMatch();
}
if (m.Success)
{
if (!exclusions.Contains(m.Value.ToLower()))
wordLengths[m.Value.Length]++;
indexPos += m.Length + 1;
}
}
catch (RegexMatchTimeoutException e)
{
if (e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT)
{
timeoutInterval += INCREMENT;
init = false;
}
else
{
// Rethrow the exception.
throw;
}
}
} while (m.Success);
// If regex completed successfully, calculate number of words and average length.
int nWords = 0;
long totalLength = 0;
for (int ctr = wordLengths.GetLowerBound(0); ctr <= wordLengths.GetUpperBound(0); ctr++)
{
nWords += wordLengths[ctr];
totalLength += ctr * wordLengths[ctr];
}
return new Tuple<int, double>(nWords, totalLength / nWords);
}
}
Imports System.Collections.Generic
Imports System.IO
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim util As New RegexUtilities()
Dim title As String = "Doyle - The Hound of the Baskervilles.txt"
Try
Dim info = util.GetWordData(title)
Console.WriteLine("Words: {0:N0}", info.Item1)
Console.WriteLine("Average Word Length: {0:N2} characters", info.Item2)
Catch e As IOException
Console.WriteLine("IOException reading file '{0}'", title)
Console.WriteLine(e.Message)
Catch e As RegexMatchTimeoutException
Console.WriteLine("The operation timed out after {0:N0} milliseconds",
e.MatchTimeout.TotalMilliseconds)
End Try
End Sub
End Module
Public Class RegexUtilities
Public Function GetWordData(filename As String) As Tuple(Of Integer, Double)
Const MAX_TIMEOUT As Integer = 1000 ' Maximum timeout interval in milliseconds.
Const INCREMENT As Integer = 350 ' Milliseconds increment of timeout.
Dim exclusions As New List(Of String)({"a", "an", "the"})
Dim wordLengths(30) As Integer ' Allocate an array of more than ample size.
Dim input As String = Nothing
Dim sr As StreamReader = Nothing
Try
sr = New StreamReader(filename)
input = sr.ReadToEnd()
Catch e As FileNotFoundException
Dim msg As String = String.Format("Unable to find the file '{0}'", filename)
Throw New IOException(msg, e)
Catch e As IOException
Throw New IOException(e.Message, e)
Finally
If sr IsNot Nothing Then sr.Close()
End Try
Dim timeoutInterval As Integer = INCREMENT
Dim init As Boolean = False
Dim rgx As Regex = Nothing
Dim m As Match = Nothing
Dim indexPos As Integer = 0
Do
Try
If Not init Then
rgx = New Regex("\b\w+\b", RegexOptions.None,
TimeSpan.FromMilliseconds(timeoutInterval))
m = rgx.Match(input, indexPos)
init = True
Else
m = m.NextMatch()
End If
If m.Success Then
If Not exclusions.Contains(m.Value.ToLower()) Then
wordLengths(m.Value.Length) += 1
End If
indexPos += m.Length + 1
End If
Catch e As RegexMatchTimeoutException
If e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT Then
timeoutInterval += INCREMENT
init = False
Else
' Rethrow the exception.
Throw
End If
End Try
Loop While m.Success
' If regex completed successfully, calculate number of words and average length.
Dim nWords As Integer
Dim totalLength As Long
For ctr As Integer = wordLengths.GetLowerBound(0) To wordLengths.GetUpperBound(0)
nWords += wordLengths(ctr)
totalLength += ctr * wordLengths(ctr)
Next
Return New Tuple(Of Integer, Double)(nWords, totalLength / nWords)
End Function
End Class
Tangkap hanya jika diperlukan
".NET mendukung konstruksi pengelompokan ekspresi reguler, yang memungkinkan Anda mengelompokkan pola ekspresi reguler menjadi satu atau lebih subekspresi." Konstruksi pengelompokan yang paling umum digunakan dalam bahasa ekspresi reguler .NET adalah (subekspresi), yang menentukan grup penangkapan bernomor, dan (?<subekspresi>nama), yang mendefinisikan grup pengambilan bernama. Konstruksi pengelompokan sangat penting untuk membuat backreference dan untuk menentukan subekspresi tempat kuantifier diterapkan.
Namun, penggunaan elemen bahasa ini memiliki biaya. Mereka menyebabkan objek GroupCollection yang dikembalikan oleh properti Match.Groups diisi dengan tangkapan tanpa nama atau tangkapan bernama terbaru. Jika konstruksi pengelompokan tunggal yang telah menangkap beberapa substring dalam string input, mereka juga mengisi objek CaptureCollection yang dikembalikan oleh properti Group.Captures dari grup penangkapan tertentu dengan beberapa objek Capture.
Seringkali, konstruksi pengelompokan hanya digunakan dalam ekspresi reguler sehingga kuantifer dapat diterapkan padanya. Kelompok yang ditangkap oleh subekspresi ini tidak digunakan kemudiannya. Misalnya, ekspresi reguler \b(\w+[;,]?\s?)+[.?!] dirancang untuk menangkap seluruh kalimat. Tabel berikut ini menjelaskan elemen bahasa dalam pola ekspresi reguler ini serta efeknya pada objek Match dan koleksi Match.Groups dan Group.Captures.
| Pola | Deskripsi |
|---|---|
\b |
Mulai pencocokan pada batas kata. |
\w+ |
Cocok dengan satu atau lebih karakter penyusun kata. |
[;,]? |
Cocok dengan nol atau satu koma atau titik koma. |
\s? |
Cocok dengan nol atau satu karakter spasi putih. |
(\w+[;,]?\s?)+ |
Mencocokkan satu atau lebih kejadian karakter kata yang diikuti oleh koma atau titik koma opsional yang diikuti oleh karakter spasi opsional. Pola ini mendefinisikan grup penangkapan pertama, yang diperlukan agar kombinasi beberapa karakter kata (yaitu, kata) diikuti oleh simbol tanda baca opsional akan diulang hingga mesin ekspresi reguler mencapai akhir kalimat. |
[.?!] |
Mencocokkan dengan titik, tanda tanya, atau tanda seru. |
Seperti yang ditunjukkan contoh berikut, ketika kecocokan ditemukan, objek GroupCollection dan CaptureCollection diisi dengan tangkapan dari kecocokan. Dalam hal ini, grup (\w+[;,]?\s?) penangkapan ada sehingga + kuantifier dapat diterapkan padanya, yang memungkinkan pola ekspresi regular agar dapat mencocokkan setiap kata dalam sebuah kalimat. Jika tidak, itu akan cocok dengan kata terakhir dalam kalimat.
using System;
using System.Text.RegularExpressions;
public class Group1Example
{
public static void Main()
{
string input = "This is one sentence. This is another.";
string pattern = @"\b(\w+[;,]?\s?)+[.?!]";
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine($"Match: '{match.Value}' at index {match.Index}.");
int grpCtr = 0;
foreach (Group grp in match.Groups)
{
Console.WriteLine($" Group {grpCtr}: '{grp.Value}' at index {grp.Index}.");
int capCtr = 0;
foreach (Capture cap in grp.Captures)
{
Console.WriteLine($" Capture {capCtr}: '{cap.Value}' at {cap.Index}.");
capCtr++;
}
grpCtr++;
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: 'This is one sentence.' at index 0.
// Group 0: 'This is one sentence.' at index 0.
// Capture 0: 'This is one sentence.' at 0.
// Group 1: 'sentence' at index 12.
// Capture 0: 'This ' at 0.
// Capture 1: 'is ' at 5.
// Capture 2: 'one ' at 8.
// Capture 3: 'sentence' at 12.
//
// Match: 'This is another.' at index 22.
// Group 0: 'This is another.' at index 22.
// Capture 0: 'This is another.' at 22.
// Group 1: 'another' at index 30.
// Capture 0: 'This ' at 22.
// Capture 1: 'is ' at 27.
// Capture 2: 'another' at 30.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is one sentence. This is another."
Dim pattern As String = "\b(\w+[;,]?\s?)+[.?!]"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index)
Dim grpCtr As Integer = 0
For Each grp As Group In match.Groups
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index)
Dim capCtr As Integer = 0
For Each cap As Capture In grp.Captures
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index)
capCtr += 1
Next
grpCtr += 1
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' Match: 'This is one sentence.' at index 0.
' Group 0: 'This is one sentence.' at index 0.
' Capture 0: 'This is one sentence.' at 0.
' Group 1: 'sentence' at index 12.
' Capture 0: 'This ' at 0.
' Capture 1: 'is ' at 5.
' Capture 2: 'one ' at 8.
' Capture 3: 'sentence' at 12.
'
' Match: 'This is another.' at index 22.
' Group 0: 'This is another.' at index 22.
' Capture 0: 'This is another.' at 22.
' Group 1: 'another' at index 30.
' Capture 0: 'This ' at 22.
' Capture 1: 'is ' at 27.
' Capture 2: 'another' at 30.
Saat Anda menggunakan subekspresi hanya untuk menerapkan kuantifier ke mereka dan Anda tidak tertarik dengan teks yang diambil, Anda harus menonaktifkan pengambilan grup. Misalnya, elemen bahasa (?:subexpression) mencegah grup penerapannya menangkap substring yang cocok. Dalam contoh berikut, pola ekspresi reguler dari contoh sebelumnya diubah menjadi \b(?:\w+[;,]?\s?)+[.?!]. Seperti yang ditunjukkan oleh output, ini mencegah mesin ekspresi reguler mengisi koleksi GroupCollection dan CaptureCollection.
using System;
using System.Text.RegularExpressions;
public class Group2Example
{
public static void Main()
{
string input = "This is one sentence. This is another.";
string pattern = @"\b(?:\w+[;,]?\s?)+[.?!]";
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine($"Match: '{match.Value}' at index {match.Index}.");
int grpCtr = 0;
foreach (Group grp in match.Groups)
{
Console.WriteLine($" Group {grpCtr}: '{grp.Value}' at index {grp.Index}.");
int capCtr = 0;
foreach (Capture cap in grp.Captures)
{
Console.WriteLine($" Capture {capCtr}: '{cap.Value}' at {cap.Index}.");
capCtr++;
}
grpCtr++;
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: 'This is one sentence.' at index 0.
// Group 0: 'This is one sentence.' at index 0.
// Capture 0: 'This is one sentence.' at 0.
//
// Match: 'This is another.' at index 22.
// Group 0: 'This is another.' at index 22.
// Capture 0: 'This is another.' at 22.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is one sentence. This is another."
Dim pattern As String = "\b(?:\w+[;,]?\s?)+[.?!]"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index)
Dim grpCtr As Integer = 0
For Each grp As Group In match.Groups
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index)
Dim capCtr As Integer = 0
For Each cap As Capture In grp.Captures
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index)
capCtr += 1
Next
grpCtr += 1
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' Match: 'This is one sentence.' at index 0.
' Group 0: 'This is one sentence.' at index 0.
' Capture 0: 'This is one sentence.' at 0.
'
' Match: 'This is another.' at index 22.
' Group 0: 'This is another.' at index 22.
' Capture 0: 'This is another.' at 22.
Anda dapat menonaktifkan pengambilan dengan salah satu cara berikut:
Gunakan elemen bahasa
(?:subexpression). Elemen ini mencegah penangkapan substring yang cocok dalam grup tempat elemen ini diterapkan. Ini tidak menonaktifkan pengambilan substring dalam grup berlapis mana pun.Gunakan ExplicitCapture opsi tersebut. Ini menonaktifkan semua tangkapan yang tidak diberi nama atau bersifat implisit dalam pola ekspresi reguler. Saat Anda menggunakan opsi ini, hanya substring yang cocok dengan grup bernama yang didefinisikan dengan elemen bahasa
(?<name>subexpression)dapat diambil. Flag ExplicitCapture dapat diteruskan ke parameteroptionskonstruktor kelas Regex atau ke parameteroptionsmetode pencocokan statis Regex.Gunakan opsi
ndalam elemen bahasa(?imnsx). Opsi ini menonaktifkan semua pengambilan yang tidak disebutkan namanya atau implisit dari titik dalam pola ekspresi reguler tempat elemen muncul. Pengambilan dinonaktifkan hingga akhir pola atau hingga opsi(-n)mengaktifkan pengambilan tanpa nama atau implisit. Untuk informasi selengkapnya, lihat Konstruksi Lain-lain.Gunakan opsi
ndalam elemen bahasa(?imnsx:subexpression). Opsi ini menonaktifkan semua tangkapan yang tidak disebutkan namanya atau implisit disubexpression. Pengambilan oleh grup pengambilan berlapis yang tidak disebutkan namanya atau implisit juga dinonaktifkan.
Keamanan Benang
Kelas Regex itu sendiri aman terhadap thread dan tidak dapat diubah (hanya-baca). Artinya, Regex objek dapat dibuat pada utas apa pun dan dibagikan di antara utas; metode yang cocok dapat dipanggil dari utas apa pun dan tidak pernah mengubah status global apa pun.
Namun, objek hasil (Match dan MatchCollection) yang dikembalikan oleh Regex harus digunakan pada satu utas. Meskipun banyak dari objek ini secara logis tidak dapat diubah, implementasinya dapat menunda komputasi beberapa hasil untuk meningkatkan performa, dan akibatnya, penelepon harus menserialisasikan akses ke objek tersebut.
Jika Anda perlu membagikan Regex objek hasil di beberapa utas, objek ini dapat dikonversi ke instans yang aman untuk utas dengan menggunakan metode yang disinkronkan. Dengan pengecualian enumerator, semua kelas ekspresi reguler aman utas atau dapat dikonversi menjadi objek aman utas dengan metode tersinkronisasi.
Para enumerator adalah satu-satunya pengecualian. Anda harus membuat serial panggilan ke enumerator koleksi. Aturannya adalah bahwa jika koleksi dapat dienumerasi pada lebih dari satu thread secara bersamaan, Anda harus menyinkronkan metode dari enumerator pada objek akar koleksi yang dilalui oleh enumerator.
Artikel terkait
| Judul | Deskripsi |
|---|---|
| Detail Perilaku Ekspresi Reguler | Memeriksa implementasi mesin ekspresi reguler di .NET. Artikel ini berfokus pada fleksibilitas ekspresi reguler dan menjelaskan tanggung jawab pengembang untuk memastikan operasi mesin ekspresi reguler yang efisien dan kuat. |
| Backtracking | Menjelaskan apa itu backtracking dan bagaimana pengaruhnya terhadap performa ekspresi reguler, dan memeriksa elemen bahasa yang memberikan alternatif untuk backtracking. |
| Bahasa Regex - Referensi Cepat | Menjelaskan elemen bahasa ekspresi reguler di .NET dan menyediakan tautan ke dokumentasi terperinci untuk setiap elemen bahasa. |