web-dev-qa-db-ja.com

\ dは[0-9]よりも効率が悪いです。

私は昨日、誰かが[0123456789][0-9]ではなく 正規表現 の中で\dを使ったという答えについてコメントしました。文字セットよりも範囲指定または数字指定を使用する方がおそらくより効率的であると私は言った。

私は今日それをテストすることに決めました、そして(少なくともC#正規表現エンジンにおいて)\dは他の2つのどちらよりも効率が悪いように思われることに驚きました。これは実際に数字を含む5077で1000のランダムな文字の10000のランダムな文字列上の私のテスト出力です:

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

2つの理由から私は驚きです。

  1. 範囲はセットよりはるかに効率的に実装されるだろうと私は考えていたでしょう。
  2. なぜ\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;
        }
    }
}
1185
weston

\dはすべてのUnicode数字をチェックしますが、[0-9]はこれらの10文字に制限されています。例えば、 ペルシア語 digits ۱۲۳۴۵۶۷۸۹は、\dと一致するが[0-9]とは一致しないUnicode数字の例です。

次のコードを使用して、そのようなすべての文字のリストを生成できます。

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

どれが生成されます:

0123456789

1511
Sina Iravanian

ドキュメントでこれに気付いたことで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
261
weston

From 正規表現の "\ d"は数字を意味しますか?

[0-9]\dと同じではありません。 [0-9]0123456789文字のみに一致しますが、\d[0-9]およびその他の数字に一致します。例えば、東アラビア数字٠١٢٣٤٥٦٧٨٩

111
İsmet Alkan

トップへの回答 from - Sina Iravianian 、ここに彼のコードの.NET 4.5バージョンがあります(そのバージョンのみがUTF16出力をサポートしているので、最初の3行を参照)。ポイント上位の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();
}

次のような出力が得られます。

DecimalDigitNumber012345678901234567890123456789߀߁߂߃߄߅߆߇߈߉012345678901২345678901234567890123456789୦୧୨୩୪୫୬୭୮୯0123456789012345678901234567890123456789෦෧෨෩෪෫෬෭෮෯012345678901234567890123456789012345678901234567890123456789᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹꩐꩑꩒ 1201234567789 ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????

レターナンバー

ⅱⅠⅡⅢⅣⅤⅥⅦⅦⅥⅪⅫⅬⅭⅮⅯⅪⅫⅬⅭⅮⅯⅪⅫⅬⅭⅮⅯⅱⅡ?〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????

OtherNumber²³¹¼½¾৴৵৶.৸৹୲୳୴୵୶୷௰௱௲౸౹౺౻౼౽౾൰൱൲൳൴൵༪༫༬༭༮༯༰༱༲༳፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼៰៱៲៳៴៵៶៷៸៹᧚⁰⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟↉①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓⳽㆒㆓㆔㆕㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㉈㉉㉊㉋㉌㉍㉎㉏㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿꠰꠱꠲꠳꠴꠵???????????????????????????????????????????? ????????????????????????????????????????????? ??????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????????????????????????????????????????????? ????

17
Sebastian

\ dはすべてのUnicodeをチェックしますが、[0-9]はこれらの10文字に制限されています。たった10桁ならば、あなたは使うべきです。その他\ dを使うことをお勧めします。

0
dengkai