Как мне сопоставить строку в кавычках в стиле CSV в nom? - PullRequest
0 голосов
/ 07 июня 2018

Строка в кавычках в стиле CSV для целей этого вопроса представляет собой строку, в которой:

  1. Строка начинается и заканчивается ровно одной ".
  2. Двадвойные кавычки внутри строки свернуты в одну двойную кавычку."Alo""ha"Alo"ha.
  3. "" само по себе является пустой строкой.
  4. Входы ошибок, такие как "A""" e", не могут быть проанализированы.Это A", за которым следует мусор e".

Я пробовал несколько вещей, ни одна из которых не сработала полностью.

Ближайшее, что я получил, спасибок какой-то помощи от пользователя pinkieval в #nom на IRC Mozilla:

use std::error as stderror; /* Avoids needing nightly to compile */

named!(csv_style_string<&str, String>, map_res!(
   terminated!(tag!("\""), not!(peek!(char!('"')))),
   csv_string_to_string
));

fn csv_string_to_string(s: &str) -> Result<String, Box<stderror::Error>> {
   Ok(s.to_string().replace("\"\"", "\""))
}

Это неправильно обрабатывает конец строки.

Я также пытался использовать re_match! макрос с r#""([^"]|"")*""#, но это всегда приводит к Err::Incomplete(1).

. Я определил, что приведенный пример CSV для Nom 1.0 не работает для строки CSV в кавычкахкак я это описываю, но я знаю, что реализации отличаются.

1 Ответ

0 голосов
/ 10 июня 2018

Вот один из способов сделать это:

use nom::types::CompleteStr;

use nom::*;

named!(csv_style_string<CompleteStr, String>,
    delimited!(
        char!('"'),
        map!(
            many0!(
                alt!(
                    // Eat a " delimiter and  the " that follows it
                    tag!("\"\"") => { |_| '"' }

                |    // Normal character
                    none_of!("\"")
                )
            ),
             // Make a string from a vector of chars
            |v| v.iter().collect::<String>()
        ),
        char!('"')
    )
);

fn main() {
    println!(r#""Alo\"ha" = {:?}"#, csv_style_string(CompleteStr(r#""Alo""ha""#)));
    println!(r#""" = {:?}"#, csv_style_string(CompleteStr(r#""""#)));
    println!(r#"bad format: {:?}"#, csv_style_string(CompleteStr(r#""A""" e""#)));
}

(я написал это полностью, но решение, подобное вашему, основанное на внешней функции вместо map!() каждого символа, тоже будет работать, и может быть более эффективным.)

Волшебство здесь, которое также решило бы вашу проблему регулярного выражения, состоит в использовании CompleteStr.Это в основном говорит nom, что ничего не придет после этого ввода (в противном случае nom предполагает, что вы выполняете потоковый парсер, поэтому может последовать больше ввода).

Это необходимо, потому что нам нужно знать, чтоделать с ", если это последний символ, введенный в nom.В зависимости от символа, который следует за ним (другой ", нормальный символ или EOF), мы должны принять другое решение - следовательно, результат Incomplete, означающий, что nom не имеет достаточного ввода, чтобы сделатьрешение.Сообщение nom о том, что EOF будет следующим, решит эту нерешительность.

Дополнительная информация по Incomplete в блоге автора nom: http://unhandledexpression.com/general/2018/05/14/nom-4-0-faster-safer-simpler-parsers.html#dealing-with-incomplete-usage


Вы можете заметить, что этоПарсер на самом деле не отклоняет неверный ввод, но анализирует начало и возвращает остаток.Если вы используете этот синтаксический анализатор в качестве подпарамера в другом парсере, последний затем передаст остаток следующему подпарсеру, который также будет аварийно завершать работу (поскольку он будет ожидать запятую), вызывая сбой всего синтаксического анализатора.

Если вы не хотите этого, вы можете сделать csv_style_string матч peek!(alt!(char!(',')|char!('\n")|eof!())).

...