web-dev-qa-db-fra.com

\ d est moins efficace que [0-9]

Hier, j'ai commenté une réponse dans laquelle une personne avait utilisé [0123456789] dans un expression régulière plutôt que [0-9] ou \d. J'ai dit qu'il était probablement plus efficace d'utiliser un spécificateur de plage ou de chiffre qu'un jeu de caractères.

J’ai décidé de tester cela aujourd’hui et j’ai découvert à ma grande surprise que (dans le moteur de calcul regex C # au moins) \d semble être moins efficace que l’un ou l’autre des deux autres qui ne semblent pas trop différer beaucoup. Voici ma sortie de test sur 10000 chaînes aléatoires de 1000 caractères aléatoires, dont 5077 contiennent en fait un chiffre:

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

C'est une surprise pour moi pour deux raisons:

  1. J'aurais pensé que la gamme serait mise en œuvre beaucoup plus efficacement que l'ensemble.
  2. Je ne comprends pas pourquoi \d est pire que [0-9]. Existe-t-il plus à \d que simplement un raccourci pour [0-9]?

Voici le code de test:

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

\d vérifie tous les chiffres Unicode, alors que [0-9] est limité à ces 10 caractères. Par exemple, Persian digits, ۱۲۳۴۵۶۷۸۹, est un exemple de chiffres Unicode qui correspondent à \d, mais pas [0-9].

Vous pouvez générer une liste de tous ces caractères à l'aide du code suivant:

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

Qui génère:

0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८ ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙ ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙0123456789

1519
Sina Iravanian

Nous remercions ByteBlast de l'avoir remarqué dans la documentation. Il suffit de changer le constructeur de regex:

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

Donne de nouveaux horaires:

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

De “\ d” dans regex signifie-t-il un chiffre? :

[0-9] n'est pas équivalent à \d. [0-9] correspond uniquement aux 0123456789 caractères, alors que \d correspond à [0-9] et à d'autres caractères numériques, par exemple les chiffres en arabe oriental ٠١٢٣٤٥٦٧٨٩

112
İsmet Alkan

Un ajout à réponse du haut de Sina Iravianian , voici une version .NET 4.5 (puisque seule cette version supporte la sortie UTF16, cf. les trois premières lignes) de son code, en utilisant la gamme complète de points de code Unicode. En raison du manque de prise en charge appropriée pour les plans Unicode supérieurs, de nombreuses personnes ne sont pas conscientes de la nécessité de toujours rechercher et inclure les plans Unicode supérieurs. Néanmoins, ils contiennent parfois des caractères importants.

Mettre à jour

Puisque \d ne prend pas en charge les caractères non-BMP dans les expressions rationnelles (merci xanatos ), voici une version qui utilise la base de données de caractères 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();
}

Donner la sortie suivante:

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

LettreNuméro

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

OtherNumber ²³¹¼½¾৴৵৶৷৸৹? ??????????????????????????????????????????????????? ??????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ????

17
Sebastian

\ d vérifie tout le Unicode, alors que [0-9] est limité à ces 10 caractères. Si seulement 10 chiffres, vous devriez utiliser. Autres Je recommande d'utiliser\d , Parce que moins d'écriture.

0
dengkai