Более эффективный Regex или альтернатива? - PullRequest
4 голосов
/ 05 февраля 2009

У меня есть файл с немногим более миллиона строк.

 {<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceVolume> "693702"^^<xsd:long>}
 {<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceId> <uri::20fb8f7d-30ef-dd11-a78d-001f29e570a8>}

Каждая строка - это утверждение.

struct Statement
    string C;
    string S;
    string P;
    string O;
    string T;

В настоящее время я использую TextReader в цикле while и анализирую каждую строку с помощью регулярного выражения:

Regex lineParse = new Regex(@"[^<|\""]*\w[^>\""]*", RegexOptions.Singleline | RegexOptions.Compiled);

Требуется довольно много времени, чтобы выполнить этот анализ, и я надеюсь, что кто-то может указать мне на более эффективную стратегию анализа.

В некоторых строках 5 совпадений, а в некоторых 4. Вот как анализируется каждая строка:

{<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceVolume> "693702"^^<xsd:long>}

Statement()
    C = uri::rdfserver#null
    S = uri::d41d8cd98f00b204e9800998ecf8427e
    P = uri::TickerDailyPriceVolume
    O = 693702
    T = xsd:long

{<uri::rdfserver#null> <uri::d41d8cd98f00b204e9800998ecf8427e> <uri::TickerDailyPriceId> <uri::20fb8f7d-30ef-dd11-a78d-001f29e570a8>}

Statement()
    C = uri::rdfserver#null
    S = uri::d41d8cd98f00b204e9800998ecf8427e
    P = uri::TickerDailyPriceId
    O = uri::20fb8f7d-30ef-dd11-a78d-001f29e570a8

Дополнительная информация из комментариев: «Низкая производительность, которую я видел, на самом деле вызвана условной точкой останова, которую я установил в коде. Без этой точки останова все довольно быстро. Тем не менее, если у кого-то есть идеи по улучшению Мне было бы интересно "- Эрик Шуновер

Ответы [ 4 ]

19 голосов
/ 05 февраля 2009

Самый быстрый (как показано ниже) простой разбиение строки:

line.Split(new char[] { '{', '<', '>', '}', ' ', '^', '"' },
           StringSplitOptions.RemoveEmptyEntries);

Следующим быстрым является закрепленное регулярное выражение (безобразно):

Regex lineParse
    = new Regex(@"^\{(<([^>]+)>\s*){3,4}(""([^""]+)""\^\^<([^>]+)>\s*)?\}$",
                RegexOptions.Compiled);
Match m = lineParse.Match(line);
if (m.Groups[2].Captures.Count == 3)
{
    Data data = new Data { C = m.Groups[2].Captures[0].Value,
        S = m.Groups[2].Captures[1].Value, P = m.Groups[2].Captures[2].Value,
        O = m.Groups[4].Value, T = m.Groups[5].Value };
} else {
    Data data = new Data { C = m.Groups[2].Captures[0].Value,
        S = m.Groups[2].Captures[1].Value, P = m.Groups[2].Captures[2].Value,
        O = m.Groups[2].Captures[3].Value, T = String.Empty };
}

Время для 1M строк случайных данных (String.Split в качестве базовой линии):

Method                #1  Wall ( Diff)     #2  Wall ( Diff)
------------------------------------------------------------
line.Split                3.6s (1.00x)         3.1s (1.00x)
myRegex.Match             5.1s (1.43x)         3.3s (1.10x)
itDependsRegex.Matches    6.8s (1.85x)         4.4s (1.44x)
stateMachine              8.4s (2.34x)         5.6s (1.82x)
alanM.Matches             9.1s (2.52x)         7.8s (2.56x)
yourRegex.Matches        18.3s (5.06x)        12.1s (1.82x)

Обновлено для включения регулярных выражений @AlanM и @itdepends. Похоже, что Regex.Matches медленнее, чем Regex.Match, однако, чем больше контекстных подсказок вы дадите парсеру, тем лучше он будет работать. Единственный класс отрицательных символов, используемый @AlanM, прост для чтения, но медленнее, чем самый загадочный (мой). Снимаю шляпу перед @itdepends для самого простого регулярного выражения, которое дает самое быстрое время. Хорошо, и хотя я думал, что было бы сумасшествием писать конечный автомат для разбора строки, на самом деле он вовсе не работает плохо ... спасибо @RexM за предложение. Я также добавил время от моего Q6600 дома (# 2) против старшего Xeon на работе (# 1).

6 голосов
/ 05 февраля 2009

Иногда конечный автомат значительно быстрее, чем Regex.

2 голосов
/ 05 февраля 2009

После некоторого тестирования я придумал:

@"<(?<capture>[^>]+)>|""(?<capture>[^""]+)"""

Значение должно быть доступно с помощью match.Groups [1] .Value.

В моем ненаучном тесте он был примерно на 75-80% быстрее, чем в первоначальном вопросе.

Матч против Матчей

В производстве я обычно использую Match, но для этого использовал Matches. Я никогда не задумывался о влиянии на производительность, поэтому провел небольшое тестирование, так что с таким же регулярным выражением :

for(Match match = regex.Match(input); match.Success; match = match.NextMatch())
// min 5.01 sec
// max 5.15 sec

foreach(Match match in regex.Matches(input))
// min 5.66 sec
// max 6.07 sec

Так что совпадение может быть быстрее, чем совпадение.

1 голос
/ 05 февраля 2009

Насколько я могу видеть, предложенные до сих пор регулярные выражения намного сложнее, чем они должны быть. Если подход разделения @sixlettervariables работает, Matches должен работать с этим регулярным выражением:

@"[^{}<> ^""]+"

Но я все же ожидал бы, что подход String.Split будет быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...