\ dは[0-9]よりも効率的ではありません


1249

昨日、誰かがor ではなく正規表現で使用[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;
        }
    }
}

178
多分\dロケールを扱います。たとえば、ヘブライ語は数字に文字を使用します。
Barmar 2013年


37
\d異なる言語で同じことを意味するわけではないため、これは興味深い質問です。たとえば、Java \dでは実際に0〜9のみに一致します
Ray Toal

17
@Barmarヘブライ語は通常、数字に文字を使用せず、同じラテン数字[0-9]を使用します。文字は数字の代わりに使用できますが、これはまれな用途であり、特別な用語のために予約されています。正規表現パーサーがכ"גיורדיסירהに一致することは期待していません(with "גは23の代替です)。また、Sina Iravanianの回答でわかるように、ヘブライ語の文字は\ dの有効な一致として表示されません。
ユヴァルアダム

7
westonのコードをJavaに移植すると、次のようになります。-Regex \ dに00:00:00.043922の結果が返されました:4912/10000-Regex [0-9]に00:00:00.073658の結果が返されました:4912/10000最初の167%-Regex [ 0123456789]は00:00:00.085799を要しました結果:4912/10000最初の195%
Lunchbox

回答:


1566

\dすべてのUnicode数字をチェックしますが、[0-9]これらの10文字に制限されています。たとえば、ペルシア語の数字は۱۲۳۴۵۶۷۸۹、とは一致するがとは一致し\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());

生成されるもの:

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


121
以下は、0〜9ではない数字のより完全なリストです。fileformat.info
Robert McKee

8
@weston Unicodeには、それぞれ16ビットの17個のプレーンがあります。最も重要な文字は基本面にありますが、一部の特殊文字(主に中国語)は補助面にあります。C#でそれらを処理するのは少し面倒です。
CodesInChaos 2013年

9
@RobertMcKee:Nitpick:完全なUnicode文字セットは実際には21ビットです(それぞれ16ビットの17プレーン)。しかし、もちろん21ビットのデータ型は実用的ではないため、2の累乗のデータ型を使用する場合、32ビットが必要なのは事実です。
sleske 2013年

3
このウィキペディアの記事によると、Unicodeコンソーシアムは、コードポイントの上限1,114,112(0〜0x010FFFF)は決して変更されないと述べています。それはunicode.orgにリンクしていますが、そこにステートメントが見つかりませんでした(おそらく私はそれを逃しただけでしょう)。
キース・トンプソン

14
変更する必要があるまで、変更されることはありません。
Robert McKee 2013

271

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

11
何をしRegexOptions.ECMAScriptますか?
ローレント

7
正規表現オプションから:「表現に対してECMAScript準拠の動作を有効にします。」
chrisaycock 2013年

28
@ 0xFE:違います。ECMAScript\u1234)でもUnicodeエスケープは有効です。意味を変えるのは「単なる」短縮文字クラスで\dあり、廃止されるのはUnicodeプロパティ/スクリプトの短縮形(など\p{N})だけです。
Tim Pietzcker 2013年

9
これは「なぜ」の部分に対する答えではありません。それは「症状を修正する」答えです。まだ貴重な情報。
usr

通常、RegrexはUnicodeマッチングをサポートしています。しかしECMAScriptはそうしません。したがって、RegexOptions.ECMAScriptを使用する場合、0〜9のASCIIにのみ一致します。
lzlstyle 2013年

119

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

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


49
によると:msdn.microsoft.com/en-us/library/20bw873z.aspx If ECMAScript-compliant behavior is specified, \d is equivalent to [0-9].
ユーザー12345678

2
ええと、私は間違っているのでしょうか、それともリンクからのこの文は反対を言っています。「\ dは任意の10進数字に一致します。これは、標準の10進数字0〜9と他の多くの文字セットの10進数字を含む\ p {Nd}正規表現パターンと同等です。」
İsmetアルカン

3
@ByteBlastのおかげで、コンストラクタを使用してvar rex = new Regex(regex, RegexOptions.ECMAScript);、パフォーマンスの点でそれらをすべてほとんど区別できなくなりました。
ウェストン2013年

2
とにかく、みんなありがとう。この質問は私にとって素晴らしい学習であることがわかりました。
İsmetアルカン

3
他の質問からの回答を「そのままコピー」しないでください。質問が重複している場合は、フラグを付けます。
BoltClock

20

Sina Iravianianからのトップアンサーに加えて、Unicodeコードポイントの全範囲を使用した、彼のコードの.NET 4.5バージョン(このバージョンのみがUTF16出力をサポートするため、最初の3行を参照)です。上位のユニコードプレーンが適切にサポートされていないため、多くの人は常に上位のユニコードプレーンをチェックして含めることを意識していません。それにもかかわらず、時にはいくつかの重要な文字が含まれています。

更新

以来\d正規表現に(感謝の非BMPの文字をサポートしていませんザナトスをここでは、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 0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८ ९ ০১২৩৪৫৬৭৮৯੦੧੨੩੪੫੬੭੮੯૦૧૨૩૪૫૬૭૮૯୦୧୨୩୪୫୬୭୮୯௦௧௨௩௪௫௬௭௮௯౦౧౨౩౪౫౬౭౮౯೦೧೨೩೪೫೬೭೮೯൦൧൨൩൪൫൬൭൮൯෦෧෨෩෪෫෬෭෮෯๐๑๒๓๔๕๖๗๘๙໐໑໒໓໔໕໖໗໘໙༠༡༢༣༤༥༦༧༨༩၀၁၂၃၄၅၆၇၈၉႐႑႒႓႔႕႖႗႘႙០១២៣៤៥៦៧៨៩᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹0123456789𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿

レター番号

ᛮᛯᛰⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↅↆↇↈ〇〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ𐅀𐅁𐅂𐅃𐅄𐅅𐅆𐅇𐅈𐅉𐅊𐅋𐅌𐅍𐅎𐅏𐅐𐅑𐅒𐅓𐅔𐅕𐅖𐅗𐅘𐅙𐅚𐅛𐅜𐅝𐅞𐅟𐅠𐅡𐅢𐅣𐅤𐅥𐅦𐅧𐅨𐅩𐅪𐅫𐅬𐅭𐅮𐅯𐅰𐅱𐅲𐅳𐅴𐍁𐍊𐏑𐏒𐏓𐏔𐏕𒐀𒐁𒐂𒐃𒐄𒐅𒐆𒐇𒐈𒐉𒐊𒐋𒐌𒐍𒐎𒐏𒐐𒐑𒐒𒐓𒐔𒐕𒐖𒐗𒐘𒐙𒐚𒐛𒐜𒐝𒐞𒐟𒐠𒐡𒐢𒐣𒐤𒐥𒐦𒐧𒐨𒐩𒐪𒐫𒐬𒐭𒐮𒐯𒐰𒐱𒐲𒐳𒐴𒐵𒐶𒐷𒐸𒐹𒐺𒐻𒐼𒐽𒐾𒐿𒑀𒑁𒑂𒑃𒑄𒑅𒑆𒑇𒑈𒑉𒑊𒑋𒑌𒑍𒑎𒑏𒑐𒑑𒑒𒑓𒑔𒑕𒑖𒑗𒑘𒑙𒑚𒑛𒑜𒑝𒑞𒑟𒑠𒑡𒑢𒑣𒑤𒑥𒑦𒑧𒑨𒑩𒑪𒑫𒑬𒑭𒑮

その他の番号²³¹¼½¾৴৵৶৷৸৹୲୳୴୵୶୷௰௱௲౸౹౺౻౼౽౾൰൱൲൳൴൵༪༫༬༭༮༯༰༱༲༳፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼៰៱៲៳៴៵៶៷៸៹᧚⁰⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟↉①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓⳽㆒㆓㆔㆕㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㉈㉉㉊㉋㉌㉍㉎㉏㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿꠰꠱꠲꠳꠴꠵𐄇𐄈𐄉𐄊𐄋𐄌𐄍𐄎𐄏𐄐𐄑𐄒𐄓𐄔𐄕𐄖𐄗𐄘𐄙𐄚𐄛𐄜𐄝𐄞𐄟𐄠𐄡𐄢𐄣𐄤𐄥𐄦𐄧𐄨𐄩𐄪𐄫𐄬𐄭𐄮𐄯𐄰𐄱𐄲𐄳𐅵𐅶𐅷𐅸𐆊𐆋𐋡𐋢𐋣𐋤𐋥𐋦𐋧𐋨𐋩𐋪𐋫𐋬𐋭𐋮𐋯𐋰𐋱𐋲𐋳𐋴𐋵𐋶𐋷𐋸𐋹𐋺𐋻𐌠𐌡𐌢𐌣𐡘𐡙𐡚𐡛𐡜𐡝𐡞𐡟𐡹𐡺𐡻𐡼𐡽𐡾𐡿𐢧𐢨𐢩𐢪𐢫𐢬𐢭𐢮𐢯𐣻𐣼𐣽𐣾𐣿𐤖𐤗𐤘𐤙𐤚𐤛𐦼𐦽𐧀𐧁𐧂𐧃𐧄𐧅𐧆𐧇𐧈𐧉𐧊𐧋𐧌𐧍𐧎𐧏𐧒𐧓𐧔𐧕𐧖𐧗𐧘𐧙𐧚𐧛𐧜𐧝𐧞𐧟𐧠𐧡𐧢𐧣𐧤𐧥𐧦𐧧𐧨𐧩𐧪𐧫𐧬𐧭𐧮𐧯𐧰𐧱𐧲𐧳𐧴𐧵𐧶𐧷𐧸𐧹𐧺𐧻𐧼𐧽𐧾𐧿𐩀𐩁𐩂𐩃𐩄𐩅𐩆𐩇𐩽𐩾𐪝𐪞𐪟𐫫𐫬𐫭𐫮𐫯𐭘𐭙𐭚𐭛𐭜𐭝𐭞𐭟𐭸𐭹𐭺𐭻𐭼𐭽𐭾𐭿𐮩𐮪𐮫𐮬𐮭𐮮𐮯𐳺𐳻𐳼𐳽𐳾𐳿𐹠𐹡𐹢𐹣𐹤𐹥𐹦𐹧𐹨𐹩𐹪𐹫𐹬𐹭𐹮𐹯𐹰𐹱𐹲𐹳𐹴𐹵𐹶𐹷𐹸𐹹𐹺𐹻𐹼𐹽𐹾𑁒𑁓𑁔𑁕𑁖𑁗𑁘𑁙𑁚𑁛𑁜𑁝𑁞𑁟𑁠𑁡𑁢𑁣𑁤𑁥𑇡𑇢𑇣𑇤𑇥𑇦𑇧𑇨𑇩𑇪𑇫𑇬𑇭𑇮𑇯𑇰𑇱𑇲𑇳𑇴𑜺𑜻𑣪𑣫𑣬𑣭𑣮𑣯𑣰𑣱𑣲𖭛𖭜𖭝𖭞𖭟𖭠𖭡𝍠𝍡𝍢𝍣𝍤𝍥𝍦𝍧𝍨𝍩𝍪𝍫𝍬𝍭𝍮𝍯𝍰𝍱𞣇𞣈𞣉𞣊𞣋𞣌𞣍𞣎𞣏🄀🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊🄋🄌


悲しいことに、Win32コンソールにはアストラル文字が表示されません
Sebastian

4
残念ながら、.NET Regexでは残念ながら非BMP文字はサポートされていません。したがって、最終的に正規表現を使用して> 0xffffの文字をチェックしても意味がありません。
xanatos 2017

-1

\ dはすべてのUnicodeをチェックしますが、[0-9]はこれらの10文字に制限されています。10桁の場合は、使用する必要があります。その他\ dを使用することをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.