Zrobiłem komentarz wczoraj na odpowiedź, gdzie ktoś użył [0123456789]
w wyrażeniu regularnym zamiast [0-9]
lub \d
. Powiedziałem, że prawdopodobnie bardziej efektywne jest użycie specyfikatora zakresu lub cyfry niż zestawu znaków.
Postanowiłem to dzisiaj przetestować i ku mojemu zaskoczeniu odkryłem, że (przynajmniej w silniku regex C #) \d
wydaje się być mniej wydajny niż którykolwiek z dwóch pozostałych, które nie wydają się bardzo różne. Oto mój wynik testu ponad 10000 losowych ciągów 1000 losowych znaków, przy czym 5077 faktycznie zawiera cyfrę:
Regular expression \d took 00:00:00.2141226 result: 5077/10000
Regular expression [0-9] took 00:00:00.1357972 result: 5077/10000 63.42 % of first
Regular expression [0123456789] took 00:00:00.1388997 result: 5077/10000 64.87 % of first
Jest to dla mnie niespodzianka z dwóch powodów:
- Myślałem, że zakres zostanie wdrożony znacznie wydajniej niż zestaw.
- Nie rozumiem, dlaczego
\d
jest gorszy niż[0-9]
. Czy jest coś więcej\d
niż tylko skrót[0-9]
?
Oto kod testowy:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace SO_RegexPerformance
{
class Program
{
static void Main(string[] args)
{
var rand = new Random(1234);
var strings = new List<string>();
//10K random strings
for (var i = 0; i < 10000; i++)
{
//Generate random string
var sb = new StringBuilder();
for (var c = 0; c < 1000; c++)
{
//Add a-z randomly
sb.Append((char)('a' + rand.Next(26)));
}
//In roughly 50% of them, put a digit
if (rand.Next(2) == 0)
{
//Replace one character with a digit, 0-9
sb[rand.Next(sb.Length)] = (char)('0' + rand.Next(10));
}
strings.Add(sb.ToString());
}
var baseTime = testPerfomance(strings, @"\d");
Console.WriteLine();
var testTime = testPerfomance(strings, "[0-9]");
Console.WriteLine(" {0:P2} of first", testTime.TotalMilliseconds / baseTime.TotalMilliseconds);
testTime = testPerfomance(strings, "[0123456789]");
Console.WriteLine(" {0:P2} of first", testTime.TotalMilliseconds / baseTime.TotalMilliseconds);
}
private static TimeSpan testPerfomance(List<string> strings, string regex)
{
var sw = new Stopwatch();
int successes = 0;
var rex = new Regex(regex);
sw.Start();
foreach (var str in strings)
{
if (rex.Match(str).Success)
{
successes++;
}
}
sw.Stop();
Console.Write("Regex {0,-12} took {1} result: {2}/{3}", regex, sw.Elapsed, successes, strings.Count);
return sw.Elapsed;
}
}
}
c#
regex
performance
weston
źródło
źródło
\d
zajmuje się lokalizacjami. Na przykład hebrajski używa liter do cyfr.\d
że nie oznacza tego samego w różnych językach. Na przykład w Javie\d
rzeczywiście pasuje tylko 0-9Odpowiedzi:
\d
sprawdza wszystkie cyfry Unicode, a[0-9]
ogranicza się do tych 10 znaków. Na przykład cyfry perskie۱۲۳۴۵۶۷۸۹
, są przykładem cyfr Unicode, które są dopasowane\d
, ale nie[0-9]
.Możesz wygenerować listę wszystkich takich znaków, używając następującego kodu:
Co generuje:
źródło
Podziękowania dla ByteBlast za zauważenie tego w dokumentacji. Wystarczy zmienić konstruktor wyrażeń regularnych:
Daje nowe czasy:
źródło
RegexOptions.ECMAScript
zrobić?ECMAScript
(\u1234
). To „tylko” stenograficzne klasy znaków, które zmieniają znaczenie (jak\d
) i skrócone właściwości (/) Unicode, które znikają (jak\p{N}
).Od Czy „\ d” w wyrażeniu regularnym oznacza cyfrę? :
źródło
If ECMAScript-compliant behavior is specified, \d is equivalent to [0-9].
var rex = new Regex(regex, RegexOptions.ECMAScript);
sprawia, że wszystkie są prawie nie do odróżnienia pod względem wydajności.Dodatek do górnej odpowiedź od Sina Iravianian , tutaj jest wersja .NET 4.5 (ponieważ tylko tego wyjścia podpory wersja UTF16, cf pierwszych trzech linii) jego kod, wykorzystując pełną gamę punktów kodowych Unicode. Z powodu braku odpowiedniego wsparcia dla wyższych samolotów Unicode, wiele osób nie jest świadomych zawsze sprawdzania i włączania wyższych samolotów Unicode. Niemniej jednak czasami zawierają ważne postacie.
Aktualizacja
Ponieważ
\d
nie obsługuje znaków innych niż BMP w wyrażeniach regularnych (dzięki xanatos ), tutaj wersja, która korzysta z bazy znaków znaków UnicodeUzyskanie następujących wyników:
źródło
Regex
nie obsługuje znaków spoza BMP. W końcu sprawdzanie znaków> 0xffff za pomocą wyrażenia regularnego jest bezużyteczne.\ d sprawdza cały Unicode, podczas gdy [0-9] jest ograniczony do tych 10 znaków. Jeśli masz tylko 10 cyfr, powinieneś użyć. Inni polecam używać \ d, ponieważ pisz mniej.
źródło