Я пытаюсь написать регулярное выражение на F #, которое будет соответствовать таким вещам

.float -.05, 2.4
.float 31.1234
.float -0.5, 1.0, 1.1

Я пробую что-то вроде этого

let matchFloat input =
    let matches = Regex(@"(\.float )?(?<float>[+-]?\d*\.\d+)").Matches(input)
    ([for m in matches -> m.Groups.["float"].Value, matches.Count > 0)

Какой вид работает, но у меня то же самое для .double, и независимо от того, какое первое в моем выражении соответствия будет совпадать, так как я делаю «встречается 0 или 1 раз», это означает, что строки с плавающей точкой номера точек, следующие за любой директивой, будут обрабатываться одинаково.

Итак, как мне убедиться, что .float существует, не выполняя input.StartsWith (...)? Я знаю, что есть способ написать это регулярное выражение, чтобы оно соответствовало надлежащим образом, и m.Groups. ["Float"]. Value вернет только то, что мне нужно, без удаления пробелов или запятых после факта.

Я возился с этой штукой часами и просто не могу заставить ее делать то, что я хочу. Я пробовал использовать функции lookbehind / lookahead и несколько других вещей, но не повезло.

1
Aaron Brewbaker 29 Сен 2014 в 05:30
Так должен ли он соответствовать первой паре чисел после .float или всем им?
 – 
hwnd
29 Сен 2014 в 06:29
Он должен соответствовать всем им
 – 
Aaron Brewbaker
29 Сен 2014 в 06:31
А .float обязательно должен встречаться?
 – 
hwnd
29 Сен 2014 в 06:31
Да, у меня также будет .double, и мне нужно уметь различать их.
 – 
Aaron Brewbaker
29 Сен 2014 в 07:19
Почему input.StartsWith(...) — плохое решение? Это определенно более читабельно и проще в реализации.
 – 
Petr
29 Сен 2014 в 19:48

3 ответа

Лучший ответ

Что ж, это поможет вам перейти к шагу 1 по ремонту Linux-машины

Вы можете использовать положительный просмотр назад в сочетании с чередованием, чтобы захватить либо .float, либо .decimal в начале строки в группу, а затем проверить, какая из них была захвачена. Сама ретроспектива не способствует первичному захвату, поэтому числовые цифры по-прежнему являются единственной вещью в «группе 0».

Тогда мой любимый трюк - добавив .* в ретроспективу (после float или decimal), вы можете успешно вернуть несколько совпадений из входной строки, каждое из которых имеет начальное .float или .decimal, но затем при каждом увеличении масштаба вперед захватывается другой набор цифр.

Надеваем на него лук с небольшим шрифтом DU, чтобы представить два случая:

type DataPoint =
    | Float of string
    | Decimal of string

let parse input =
    let patt = "(?<=^\.((float)|(decimal)).*(,?\s+))[+-]?\d*\.\d+(?=\s*(,|$))"
    Regex.Matches(input, patt)
    |> Seq.cast<Match>
    |> Seq.map (fun m ->
        match (m.Groups.[2].Success, m.Groups.[3].Success) with
        | (true, false) -> Float(m.Value)
        | (false, true) -> Decimal(m.Value)
        | _ -> failwith "??")
    |> List.ofSeq

// positive cases
parse ".float -.05, 2.4"        // [Float "-.05"; Float "2.4"]
parse ".float 31.1234"          // [Float "31.1234"]
parse ".float -0.5, 1.0, 1.1"   // [Float "-0.5"; Float "1.0"; Float "1.1"]
parse ".decimal 123.456, -22.0" // [Decimal "123.456"; Decimal "-22.0"]

// negative cases, plucks out valid bits
parse ".decimal xyz,,.., +1.0, .2.3.4, -.2 "  // [Decimal "+1.0"; Decimal "-.2"]
parse ".float 1.0, 2.0-, 3."                  // [Float "1.0"]

Обратите внимание, что я просто полагался на номера групп, вы можете быть более осторожными и использовать именованные группы.

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

Изменить: я несколько укрепил шаблон против негативных отзывов на основе отзывов. Все еще не пуленепробиваемый.

4
latkin 30 Сен 2014 в 01:26
На самом деле, ваше регулярное выражение также соответствует таким строкам, как ".float 5.4.3.,.0"
 – 
Petr
29 Сен 2014 в 23:37
Это отличный отзыв и совершенно верно! Приходит обновление.
 – 
latkin
30 Сен 2014 в 01:10

На самом деле я не вижу причин, по которым в этом случае Regex вообще может быть полезен. Использование строковых функций и функций синтаксического анализа системы является более читаемым и безопасным способом анализа ввода. Заимствование DataPoint из ответа @ latkin:

open System

type DataPoint =
    | Float of float
    | Decimal of decimal

let listparse parser mapper = 
    List.choose (fun f -> match parser f with true, v -> Some (mapper v) | _ -> None)

let parse (input: string) =
    match Array.toList (input.Split([|',';' '|])) with
    | ".float"::rest -> rest |> listparse Double.TryParse Float
    | ".decimal"::rest -> rest |> listparse Decimal.TryParse Decimal
    | _ -> []

А используя дополнительные параметры в методах .TryParse (), вы можете легко обрабатывать более сложные входные форматы с плавающей запятой, такие как представление экспоненты (например, 1.3E5)

1
Petr 30 Сен 2014 в 04:41

Попробуйте это \.float [+-]?[0-9\., ]+, а также обратитесь к этому (http://regex101.com/r/kW6zZ1/1)

-1
Menaka Sankar 29 Сен 2014 в 09:36
У этого есть несколько проблем: 1. Он соответствует поддельным строкам, таким как .float ,,,,,, .float 5.4.3.,.0 и т. д. 2. Он не захватывает все допустимые строки, такие как .float 1.5, -2.5 3. Он не дает возможности извлеките отдельные группы цифр, поскольку OP ясно дал понять, что это цель.
 – 
latkin
29 Сен 2014 в 23:12