我昨天发表了一条评论,其中有人在regular expression而不是[0123456789]
或[0-9]
使用了\d
。我说使用范围或数字说明符比使用字符集更有效。
我今天决定对此进行测试,并且我惊讶地发现(至少在C#正则表达式引擎中)\d
似乎效率低于其他两个看起来差别不大的。这是我的10000个随机字符串1000个随机字符的测试输出,其中5077实际上包含一个数字:
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
令我惊讶的是有两个原因:
\d
比[0-9]
更糟糕。 \d
还有更多内容,而不仅仅是[0-9]
的简写吗?这是测试代码:
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;
}
}
}
答案 0 :(得分:1515)
\d
检查所有Unicode数字,而[0-9]
仅限于这10个字符。例如,Persian位数۱۲۳۴۵۶۷۸۹
是与\d
匹配的Unicode数字示例,但不是[0-9]
。
您可以使用以下代码生成所有此类字符的列表:
var sb = new StringBuilder();
for(UInt16 i = 0; i < UInt16.MaxValue; i++)
{
string str = Convert.ToChar(i).ToString();
if (Regex.IsMatch(str, @"\d"))
sb.Append(str);
}
Console.WriteLine(sb.ToString());
生成:
012345678901234567890123456789߀߁߂߃߄߅߆߇߈߉012345678901২345678901234567890123456789୦୧୨୩୪୫୬୭୮୯0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙0123456789
答案 1 :(得分:261)
感谢ByteBlast在文档中注意到这一点。只需更改正则表达式构造函数:
var rex = new Regex(regex, RegexOptions.ECMAScript);
提供新时间:
Regex \d took 00:00:00.1355787 result: 5077/10000
Regex [0-9] took 00:00:00.1360403 result: 5077/10000 100.34 % of first
Regex [0123456789] took 00:00:00.1362112 result: 5077/10000 100.47 % of first
答案 2 :(得分:112)
来自Does “\d” in regex mean a digit?:
[0-9]
不等同于\d
。[0-9]
仅匹配0123456789
个字符,\d
匹配[0-9]
和其他数字字符,例如东方阿拉伯数字٠١٢٣٤٥٦٧٨٩
答案 3 :(得分:17)
来自top answer的Sina Iravianian的补充,这里是他的代码的.NET 4.5版本(因为只有该版本支持UTF16输出,例如前三行), 使用全范围的Unicode代码点。 由于缺乏对更高Unicode平面的适当支持,许多人并不知道总是检查并包括上层Unicode平面。然而,他们有时确实包含一些重要的角色。
<强>更新强>
由于\d
不支持正则表达式中的非BMP字符(感谢xanatos),因此这里使用Unicode字符数据库的版本
public static void Main()
{
var unicodeEncoding = new UnicodeEncoding(!BitConverter.IsLittleEndian, false);
Console.InputEncoding = unicodeEncoding;
Console.OutputEncoding = unicodeEncoding;
var sb = new StringBuilder();
for (var codePoint = 0; codePoint <= 0x10ffff; codePoint++)
{
var isSurrogateCodePoint = codePoint <= UInt16.MaxValue
&& ( char.IsLowSurrogate((char) codePoint)
|| char.IsHighSurrogate((char) codePoint)
);
if (isSurrogateCodePoint)
continue;
var codePointString = char.ConvertFromUtf32(codePoint);
foreach (var category in new []{
UnicodeCategory.DecimalDigitNumber,
UnicodeCategory.LetterNumber,
UnicodeCategory.OtherNumber})
{
sb.AppendLine($"{category}");
foreach (var ch in charInfo[category])
{
sb.Append(ch);
}
sb.AppendLine();
}
}
Console.WriteLine(sb.ToString());
Console.ReadKey();
}
产生以下输出:
DecimalDigitNumber 012345678901234567890123456789߀߁߂߃߄߅߆߇߈߉012345678901২345678901234567890123456789୦୧୨୩୪୫୬୭୮୯0123456789012345678901234567890123456789෦෧෨෩෪෫෬෭෮෯012345678901234567890123456789012345678901234567890123456789᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙ ꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹0123456789
LetterNumber
ᛮᛯᛰⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↅↆↇↈ〇〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ
OtherNumber ²³¹¼½¾৴৵৶.৸৹୲୳୴୵୶୷௰௱௲౸౹౺౻౼౽౾൰൱൲൳൴൵༪༫༬༭༮༯༰༱༲༳፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼៰៱៲៳៴៵៶៷៸៹᧚⁰⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟↉①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓⳽㆒㆓㆔㆕㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㉈㉉㉊㉋㉌㉍㉎㉏㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿꠰꠱꠲꠳꠴꠵
答案 4 :(得分:0)
\ d检查所有Unicode,而[0-9]仅限于这10个字符。 如果只有10位数,你应该使用。 其他我建议使用\ d,因为少写。
答案 5 :(得分:-15)
\d
效率会降低,因为必须转换才能进行比较。
例如,如果我希望Regex找到IP地址,我希望我们\d
比[0123456789]
或甚至[0-9]
代表任何数字。
一般来说,在我的正则表达式使用中,功能如果比速度更重要。