\ d [0-9] 'tan daha az verimlidir


1247

Birinin kullanılmış bir yanıtı dün bir yorum yaptı [0123456789]bir de normal ifade yerine [0-9]ya \d. Bir aralık veya basamak belirtecini kullanmanın bir karakter setinden daha etkili olduğunu söyledim.

Bunu bugün test etmeye karar verdim ve (en azından C # regex motorunda) \dçok farklı görünmeyen diğer ikisinden daha az verimli göründüğüne şaşırdım . İşte 5077 aslında bir rakam içeren 1000 rastgele karakter 10000 rastgele dizeleri üzerinde benim test çıktı:

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

İki nedenden dolayı benim için sürpriz oldu:

  1. Aralığın setten çok daha verimli uygulanacağını düşünürdüm.
  2. Neden \ddaha kötü olduğunu anlayamıyorum [0-9]. \dSadece kısaltmaktan daha fazlası var mı [0-9]?

İşte test kodu:

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
Belki yerlilerle \dilgilenir. Örneğin İbranice rakamlar için harf kullanır.
Barmar


37
Bu ilginç bir sorudur çünkü \dfarklı dillerde aynı anlama gelmez. Java'da, örneğin \dgerçekten sadece 0-9 ile eşleşir
Ray Toal

17
@Barmar İbranice normalde rakamlar için harf kullanmaz, bunun yerine aynı latin rakamları kullanır [0-9]. Harfler rakamlarla değiştirilebilir, ancak bu nadir bir kullanımdır ve özel terimlere ayrılmıştır. Normal ifade ayrıştırıcısının כ"ג יורדי סירה ile eşleşmesini beklemem (a"ג 23 yerine geçer). Ayrıca, Sina Iravanian'ın cevabında da görüldüğü gibi, İbranice mektupları \ d için geçerli eşleşmeler olarak görünmüyor.
Yuval Adam

7
Weston'un kodunu Java'ya taşıma: - Regex \ d 00: 00: 00.043922 sonucunu aldı: 4912/10000 - Regex [0-9] 00: 00: 00.073658 sonucunu aldı: 4912/10000 ilkinin% 167'sini aldı - Regex [ 0123456789] aldı 00: 00: 00.085799 sonuç: 4912/10000 ilk% 195
Lunchbox

Yanıtlar:


1565

\dtüm Unicode basamaklarını kontrol eder, ancak [0-9]bu 10 karakterle sınırlıdır. Örneğin, Farsça rakamlar, ۱۲۳۴۵۶۷۸۹ile eşleşen \d, ancak eşleşmeyen Unicode basamaklara bir örnektir [0-9].

Aşağıdaki kodu kullanarak bu tür karakterlerin bir listesini oluşturabilirsiniz:

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

Hangi üretir:

012345678901234567890123456789߀߁߂߃߄߅߆߇߈߉012345678 9 01২345678901234567890123456789 ୦୧୨୩୪୫୬୭୮୯ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙ ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙0123456789


121
0-9 olmayan rakamların daha eksiksiz bir listesi: fileformat.info/info/unicode/category/Nd/list.htm
Robert McKee

8
@weston Unicode'da her biri 16 bit olan 17 uçak vardır. En önemli karakterler temel düzlemdedir, ancak çoğunlukla Çince olan bazı özel karakterler ek düzlemlerde bulunur. C # ile başa çıkmak biraz sinir bozucu.
CodesInChaos

9
@RobertMcKee: Nitpick: Tam unicode karakter kümesi aslında 21 bittir (her biri 16 bitlik 17 uçak). Tabii ki 21 bitlik bir veri tipi pratik değildir, bu nedenle 2'lik bir güç tipi veri kullanıyorsanız 32 bite ihtiyacınız olduğu doğrudur.
sleske

3
Bu Wikipedia makalesine göre , Unicode Konsorsiyumu, 1,114,112 kod noktası (0 - 0x010FFFF) sınırının hiçbir zaman değiştirilmeyeceğini belirtti. Unicode.org'a bağlanıyor, ancak ifadeyi orada bulamadım (muhtemelen kaçırdım).
Keith Thompson

14
Asla değiştirilmeyecek - değiştirmeleri gerekene kadar.
Robert McKee

271

Belgelerde bunu fark ettiği için ByteBlast'a teşekkür ederiz. Sadece regex yapıcısını değiştirmek:

var rex = new Regex(regex, RegexOptions.ECMAScript);

Yeni zamanlamalar verir:

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
Ne yapar RegexOptions.ECMAScript?
laurent

7
Gönderen Normal İfade Seçenekleri : "ifadesi için ECMAScript uyumlu davranışlara imkan."
chrisaycock

28
@ 0xFE: Pek değil. Unicode çıkışları ECMAScript( \u1234) içinde hala geçerlidir . Anlamını değiştiren (sadece) steno karakter sınıfları ve ortadan kaybolan (gibi \d) Unicode özellik / betik kısayolları "sadece" dir \p{N}.
Tim Pietzcker

9
Bu, "neden" kısmına bir cevap değildir. Bu bir "semptomları düzeltin" cevabıdır. Hala değerli bilgiler.
usr

Genel olarak Regrex, unicode eşleştirmeyi destekler. Ancak ECMAScript bunu yapmaz. Bu nedenle, RegexOptions.ECMAScript kullanılırken, yalnızca ascii, yani 0-9 ile eşleşir.
lzlstyle

119

Kimden Normal ifade içinde “\ d” bir rakam anlamına mı geliyor? :

[0-9]eşdeğer değil \d. [0-9]yalnızca 0123456789karakterlerle eşleşirken, \deşleşmeler [0-9]ve diğer rakam karakterleri, örneğin Doğu Arapça rakamları٠١٢٣٤٥٦٧٨٩


49
Buna göre: msdn.microsoft.com/en-us/library/20bw873z.aspx If ECMAScript-compliant behavior is specified, \d is equivalent to [0-9].
Kullanıcı 12345678

2
ha, yanlış mıyım yoksa bağlantıdaki bu cümle tersini söylüyor. "\ d herhangi bir ondalık basamakla eşleşir. Standart 0-9 ondalık basamaklarının yanı sıra diğer karakter kümelerinin ondalık basamaklarını içeren \ p {Nd} normal ifade modeline eşdeğerdir."
İsmet Alkan

3
@ByteBlast, yapıcıyı kullanarak teşekkürler: var rex = new Regex(regex, RegexOptions.ECMAScript);hepsini performans açısından ayırt edilemez hale getirir.
weston

2
Her neyse, herkese teşekkürler. bu sorunun benim için çok iyi bir öğrenme olduğu ortaya çıktı.
İsmet Alkan

3
Lütfen diğer soruların cevaplarını "sadece kopyalamayın". Soru yineleniyorsa, soruyu işaretleyin.
BoltClock

20

Bir ektir üst cevabını dan Sina Iravianian , burada Unicode kod noktalarının tam aralığını kullanarak, onun kod (ilk üç satır cf sadece o sürüm destekleri UTF16 çıkışı beri) bir .NET 4.5 sürümüdür. Daha yüksek Unicode uçaklar için uygun desteğin bulunmaması nedeniyle, birçok insan her zaman üst Unicode uçakları kontrol etmenin ve dahil etmenin farkında değildir. Bununla birlikte, bazen bazı önemli karakterler içerirler.

Güncelleme

Yana \dregex olmayan BMP karakterler (teşekkürler desteklemez Xanatos ) Unicode karakter veritabanını kullanır, burada bir versiyonu

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

Aşağıdaki çıktıyı verir:

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

Mektup numarası

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

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


Üzücü olan şey, Win32 Konsolu'nun astral karakterleri göstermemesidir
Sebastian,

4
Doğru hatırlıyorsam, ne yazık ki .NET RegexBMP olmayan karakterleri desteklemiyor. Sonuçta bir regex ile> 0xffff karakterleri kontrol etmek işe yaramaz.
xanatos

-1

\ d tüm Unicode'u kontrol ederken [0-9] bu 10 karakterle sınırlıdır. Sadece 10 haneli ise kullanmalısınız. Diğerleri \ d , kullanmanızı öneririm Çünkü daha az yazıyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.