CSharp - d 的效率比[ 0-9 ] 慢?

  显示原文与译文双语对照的内容

昨天我做了一个评论在答案上有人 [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

有两个原因让我大吃一惊:

  1. 我认为这个范围会比集合更高效地实现。
  2. 我无法理解为什么 d[0-9] 更差。 d 是否比简单的shorthand 更适合 [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;
 }
 }
}

时间:

d 检查所有 Unicode 数字,而 [0-9] 仅限于这 10个字符。 例如波斯语数字,۱۲۳۴۵۶۷۸۹,是一个与 d 匹配的Unicode数字,但不是 [0-9]

你可以使用以下代码生成所有此类字符的List:


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

生成:

感谢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

"d"正规表达式 意味着数字?

[0-9]d 不等效。 [0-9] 只匹配 0123456789 字符,而 d 匹配 [0-9] 和其他数字字符,例如东部阿拉伯数字 ٠١٢٣٤٥٦٧٨٩

除了高级回答新浪Iravianian,这里是一个. NET ( 因为只有这个版本支持utf16输出, c.f. 第一个三行) 4.5版本的代码,使用全方位的unicode代码点。 由于缺乏适当的支持更高的unicode飞机,许多人没有意识到的总是检查,包括上层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);

 if (Regex.IsMatch(codePointString, @"d"))
 sb.AppendFormat("{0}", codePointString);
 }
 Console.WriteLine(sb.ToString());

 Console.ReadKey();
}

产生以下输出:


0 1 2 3 4 5 6 7 8 9 ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ߀ ߁ ߂ ߃ ߄ ߅ ߆ ߇ ߈ ߉ ० १ २ ३ ४ ५ ६ ७ ८ ९
০ ১ ২ ৩ ৪ ৫ ৬ ৭ ৮ ৯ ੦ ੧ ੨ ੩ ੪ ੫ ੬ ੭ ੮ ੯ ૦ ૧ ૨ ૩ ૪ ૫ ૬ ૭ ૮ ૯ ୦ ୧ ୨ ୩ ୪ ୫ ୬ ୭ ୮ ୯ ௦ ௧ ௨ ௩ ௪ ௫ ௬ ௭ ௮ ௯
౦ ౧ ౨ ౩ ౪ ౫ ౬ ౭ ౮ ౯ ೦ ೧ ೨ ೩ ೪ ೫ ೬ ೭ ೮ ೯ ൦ ൧ ൨ ൩ ൪ ൫ ൬ ൭ ൮ ൯
๐ ๑ ๒ ๓ ๔ ๕ ๖ ๗ ๘ ๙ ໐ ໑ ໒ ໓ ໔ ໕ ໖ ໗ ໘ ໙ ༠ ༡ ༢ ༣ ༤ ༥ ༦ ༧ ༨ ༩ ၀ ၁ ၂ ၃ ၄ ၅ ၆ ၇ ၈ ၉ ႐ ႑ ႒ ႓ ႔ ႕ ႖ ႗ ႘ ႙
០ ១ ២ ៣ ៤ ៥ ៦ ៧ ៨ ៩ ᠐ ᠑ ᠒ ᠓ ᠔ ᠕ ᠖ ᠗ ᠘ ᠙ ᥆ ᥇ ᥈ ᥉ ᥊ ᥋ ᥌ ᥍ ᥎ ᥏ ᧐ ᧑ ᧒ ᧓ ᧔ ᧕ ᧖ ᧗ ᧘ ᧙ ᭐ ᭑ ᭒ ᭓ ᭔ ᭕ ᭖ ᭗ ᭘ ᭙ ᮰ ᮱ ᮲ ᮳ ᮴ ᮵ ᮶
᮷ ᮸ ᮹ ᱀ ᱁ ᱂ ᱃ ᱄ ᱅ ᱆ ᱇ ᱈ ᱉ ᱐ ᱑ ᱒ ᱓ ᱔ ᱕ ᱖ ᱗ ᱘ ᱙ ꘠ ꘡ ꘢ ꘣ ꘤ ꘥ ꘦ ꘧ ꘨ ꘩ ꣐ ꣑ ꣒ ꣓ ꣔ ꣕ ꣖ ꣗ ꣘ ꣙ ꤀ ꤁ ꤂ ꤃ ꤄
꤅ ꤆ ꤇ ꤈ ꤉ ꩐ ꩑ ꩒ ꩓ ꩔ ꩕ ꩖ ꩗ ꩘ ꩙ 0 1 2 3 4 5 6 7 8 9

...