Советы по FParsec для обработки пробелов
У меня есть следующее подвыражение для разбора "цитаты", которые имеют следующий формат
"5.75 @ 5.95"
Поэтому у меня есть это выражение парсек для его анализа
let pquote x = (sepBy (pfloat) ((spaces .>> (pchar '/' <|> pchar '@' )>>. spaces))) x
Это работает нормально... за исключением случаев, когда в моем входе есть пробел, поскольку выражение-разделитель начинает потреблять контент. Итак, я обернул его вокруг попытки, которая работает и, насколько я понимаю, более или менее означает, что это означало быть.
let pquote x = (sepBy (pfloat) (attempt (spaces .>> (pchar '/' <|> pchar '@' )>>. spaces))) x
Поскольку я не очень хорошо знаю fparsec, мне интересно, есть ли лучший способ написать это. это кажется немного тяжелым (хотя все еще очень управляемым, конечно)
3 ответа
let s1 = "5.75 @ 5.95 "
let s2 = "5.75/5.95 "
let pquote: Parser<_> =
pfloat
.>> spaces .>> skipAnyOf ['@'; '/'] .>> spaces
.>>. pfloat
.>> spaces
Заметки:
-
я сделалspaces
необязательно вездеspaces
пропускает любую последовательность из нуля или более пробелов, поэтому нет необходимости использоватьopt
- спасибо @Daniel; type Parser<'t> = Parser<'t, UserState>
- Я определяю это таким образом, чтобы избежать ошибки "ограничение значения"; Вы можете удалить это;-
Кроме того, не забывайте следующее, если ваша программа может работать в системе с настройками языка по умолчанию с десятичной запятой:System.Threading.Thread.CurrentThread.CurrentCulture <- Globalization.CultureInfo.GetCultureInfo "en-US"
это не сработает, спасибо @Stephan - Я бы не использовал
sepBy
если у меня нет списка значений неизвестного размера. - Если вам не нужно возвращаемое значение (например,
'@'
символов), рекомендуется использоватьskip*
функции вместоp*
по соображениям производительности.
UPD добавил косую черту в качестве разделителя
Я бы, наверное, сделал что-то вроде этого, который возвращает float * float
:
let ws = spaces
let quantity = pfloat .>> ws
let price = pfloat .>> ws
let quoteSep = pstring "@" .>> ws
let quote = quantity .>> quoteSep .>>. price //`.>> eof` (if final parser)
Для каждого парсера характерно использовать конечные пробелы. Просто убедитесь, что ваш парсер верхнего уровня включает eof
,
Предполагая, что вы можете иметь более двух float
во входных данных и '/' и '@' являются разделителями:
let ws = spaces
let str_ws s = pstring s .>> ws
let float_ws = pfloat .>> ws
let pquote = sepBy float_ws (str_ws "/" <|> str_ws "@")
Говоря об обработке пробелов, этот раздел учебника FParsec действительно полезен.