it-swarm.com.de

\ d ist weniger effizient als [0-9]

Ich habe gestern einen Kommentar zu einer Antwort abgegeben, bei der jemand [0123456789] in einem regulären Ausdruck anstelle von [0-9] oder \d verwendet hat. Ich sagte, es sei wahrscheinlich effizienter, einen Bereichs- oder Ziffernbezeichner als einen Zeichensatz zu verwenden.

Ich habe mich heute entschlossen, dies zu testen, und zu meiner Überraschung festgestellt, dass \d (zumindest in der C # -Regex-Engine) weniger effizient zu sein scheint als die beiden anderen, die sich scheinbar nicht wesentlich unterscheiden. Hier ist meine Testausgabe über 10000 zufällige Zeichenfolgen mit 1000 zufälligen Zeichen, wobei 5077 tatsächlich eine Ziffer enthält:

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

Es ist aus zwei Gründen eine Überraschung für mich:

  1. Ich hätte gedacht, dass der Bereich viel effizienter als das Set implementiert werden würde.
  2. Ich kann nicht verstehen, warum \d schlechter ist als [0-9]. Gibt es mehr zu \d als nur eine Abkürzung für [0-9]?

Hier ist der Testcode:

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;
        }
    }
}
1197
weston

\d überprüft alle Unicode-Ziffern, während [0-9] auf diese 10 Zeichen begrenzt ist. Beispiel: Persisch Ziffern, ۱۲۳۴۵۶۷۸۹, sind ein Beispiel für Unicode-Ziffern, die mit \d, jedoch nicht mit [0-9] übereinstimmen.

Mit dem folgenden Code können Sie eine Liste aller dieser Zeichen erstellen:

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

Welches erzeugt:

0123456789 ٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८ ९ ০১২৩৪৫৬৭৮৯੦੧੨੩੪੫੬੭੮੯૦૧૨૩૪૫૬૭૮૯ ୦୧୨୩୪୫୬୭୮୯ ௦௧௨௩௪௫௬௭௮௯౦౧౨౩౪౫౬౭౮౯೦೧೨೩೪೫೬೭೮೯൦൧൨൩൪൫൬൭൮൯๐๑๒๓๔๕๖๗๘๙໐໑໒໓໔໕໖໗໘໙༠༡༢༣༤༥༦༧༨༩၀၁၂၃၄၅၆၇၈၉႐႑႒႓႔႕႖႗႘႙០១២៣៤៥៦៧៨៩ ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙ ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙

1526
Sina Iravanian

Wir danken ByteBlast, dass sie dies in den Dokumenten bemerkt haben. Ändern Sie einfach den Regex-Konstruktor:

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

Gibt neue Timings:

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
262
weston

Von Bedeutet "\ d" in Regex eine Ziffer? :

[0-9] entspricht nicht \d. [0-9] stimmt nur mit 0123456789 Zeichen überein, während \d mit [0-9] und anderen Ziffernzeichen übereinstimmt, zum Beispiel ostarabischen Ziffern ٠١٢٣٤٥٦٧٨٩

113
İsmet Alkan

Eine Ergänzung zu top answer von Sina Iravianian , hier ist eine .NET 4.5-Version (da nur diese Version die UTF16-Ausgabe unterstützt, siehe die ersten drei Zeilen) seines Codes mit die gesamte Palette der Unicode-Codepunkte. Aufgrund des Mangels an angemessener Unterstützung für höhere Unicode-Ebenen ist vielen Menschen nicht bewusst, dass sie immer nach den höheren Unicode-Ebenen suchen und diese einbeziehen. Trotzdem enthalten sie manchmal einige wichtige Zeichen.

Update

Da \d keine Nicht-BMP-Zeichen in Regex unterstützt (danke xanatos ), hier eine Version, die die Unicode-Zeichendatenbank verwendet

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

Die folgende Ausgabe liefern:

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

Briefnummer

ᛮᛯᛰⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↅↆↇↈ〇〡〢〣〤〥〦〧〨〩〸〹〺ꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ ???????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ?????????????????????????????????????????? ???????????????????????????????????????????

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

17
Sebastian

\ d überprüft alle Unicode-Zeichen, während [0-9] auf diese 10 Zeichen beschränkt ist. Wenn nur 10 Stellen, sollten Sie verwenden. Andere empfehle ich mit\d , Weil weniger schreiben.

0
dengkai