Вопрос по parsing, c# – Разбор без разделения строк

6

Это побочный результат обсуждения вкакой-то другой вопрос.

Предположим, мне нужно проанализировать огромное количество очень длинных строк. Каждая строка содержит последовательностьdoubles (в текстовом представлении, конечно), разделенные пробелом. Мне нужно разобратьdoubleс вList<double>.

Стандартная методика разбора (с использованиемstring.Split + double.TryParse) кажется довольно медленным: для каждого из чисел нам нужно выделить строку.

Я попытался сделать его старым C-подобным способом: вычислить индексы начала и конца подстрок, содержащих числа, и проанализировать его «на месте», не создавая дополнительную строку. (Увидетьhttp://ideone.com/Op6h0, ниже показана соответствующая часть.)

<code>int startIdx, endIdx = 0;
while(true)
{
    startIdx = endIdx;
    // no find_first_not_of in C#
    while (startIdx < s.Length && s[startIdx] == ' ') startIdx++;
    if (startIdx == s.Length) break;
    endIdx = s.IndexOf(' ', startIdx);
    if (endIdx == -1) endIdx = s.Length;
    // how to extract a double here?
}
</code>

Существует перегрузкаstring.IndexOf, поиск только внутри данной подстроки, но мне не удалось найти метод для анализа двойного от подстроки, без фактического извлечения этой подстроки в первую очередь.

У кого-нибудь есть идея?

@ Джон: не совсем. Вопрос основан на обсуждении по связанному вопросу / Stackoverflow.com вопросы / 10053449 / ...). Простите за это Vlad
Вы доказали, что на самом деле это узкое место? Я не Ноу любого способа сделать это из рук в руки, но я бы, конечно, хотел бы получить некоторые доказательства того, что это проблема перед микро-оптимизацией. Jon Skeet
Справедливо. Я подозреваю, что рукописная процедура разбора будет медленнее, чем метод, предположительно оптимизированный с большим опытом работы, который придумала команда BCL:) Jon Skeet
@ HenkHolterman Вы, вероятно, правы, что это несущественная преждевременная оптимизация во многих случаях использования. В нашем случае, когда мы не можем легко предварительно обработать большие объемы данных в более разумный формат, и нам нужно загрузить их на ограниченных платформах, мы видим значительные издержки из-за GC, вызванного непосредственно выделением в string.Split. Вопросы, стоящие за вопросом, очень важны для нас, и, я полагаю, одна из причин, по которой Span <T> введен в C # 7.2. DuneCat
@ Хенк: большое спасибо за ваш совет - но я бы воздержался от дальнейшего обсуждения, поскольку, похоже, он переходит от вопросов кодирования к личным. Vlad

Ваш Ответ

2   ответа
7

не управляемый API для парсинга двойного из подстроки. Я предполагаю, что выделение строки будет незначительным по сравнению со всеми операциями с плавающей запятой в double.Parse.

В любом случае, вы можете сохранить выделение, создав один раз буферную строку длиной 100, состоящую только из пробелов. Затем для каждой строки, которую вы хотите проанализировать, вы копируете символы в эту строку буфера, используя небезопасный код. Вы заполняете строку буфера пробелами. А для разбора вы можете использовать NumberStyles.AllowTrailingWhite, что приведет к игнорированию конечных пробелов.

Получение указателя на строку на самом деле полностью поддерживаемая операция:

    string l_pos = new string(' ', 100); //don't write to a shared string!
    unsafe 
    {
        fixed (char* l_pSrc = l_pos)
        {               
              // do some work
        }
    }

C # имеет специальный синтаксис для привязки строки к символу *.

@ usr: Хорошее наблюдение, спасибо! Vlad
Разве анализ всех этих пробелов не делает это на самом деле медленнее, чем выделение новой строки каждый раз? svick
Я правильно понимаю: ты имеешь в виду изменение предположительно неизменногоSystem.String с небезопасным кодом? Vlad
См. System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData, который в настоящее время жестко запрограммирован в 8. usr
A StringBuilder не может быть использован для анализа двойного от. Когда вы вызываете ToString, строка внутреннего буфера StringBuilders сбрасывается (если это не так, вы можете задним числом изменить строку, переданную приложению). По той же причине StringBuilder является поточно-ориентированным. usr
2

я бы использовал конечный автомат

это может выглядеть так:

enum State
{
    Separator, Sign, Mantisse etc.
}
State CurrentState = State.Separator;
int Prefix, Exponent, Mantisse;
foreach(var ch in InputString)
{
    switch(CurrentState)
    { // set new currentstate in dependence of ch and CurrentState
        case Separator:
           GotNewDouble(Prefix, Exponent, Mantisse); 


    }

}
Вы имеете в виду ручной анализ без использования TryParse? Vlad
yes, если вы используете TryParse, вам нужен каждый раз новый экземпляр строки. тогда у вас такое же поведение, как у var values = string.Split ('') .Select (s => double.Parse (s)). ToArray (); user287107
Хорошо, ручной анализ имеет тенденцию быть медленным и глючным, я хотел бы избежать повторного изобретения колеса, если это возможно. Vlad

Похожие вопросы