Как мне извлечь среднюю часть строки в FSharp? - PullRequest
6 голосов
/ 20 сентября 2010

Я хочу извлечь среднюю часть строки, используя FSharp, если она заключена в кавычки, примерно так:

let middle =
    match original with
    | "\"" + mid + "\"" -> mid
    | all -> all

Но это не работает из-за инфиксного оператора + в выражении шаблона. Как я могу извлечь это?

Ответы [ 5 ]

12 голосов
/ 20 сентября 2010

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

Ниже приведен шаблон, который принимает два параметра (префикс и строка постфикса).) и завершается успешно, если заданный ввод начинается / заканчивается указанными строками.Шаблон не завершен (может завершиться ошибкой), поэтому мы будем использовать синтаксис |Name|_|, и для этого потребуется вернуть значение параметра:

let (|Middle|_|) prefix postfix (input:string) =
  // Check if the string starts with 'prefix', ends with 'postfix' and 
  // is longer than the two (meaning that it contains some middle part)
  if input.StartsWith(prefix) && input.EndsWith(postfix) && 
     input.Length >= (prefix.Length + postfix.Length) then 
    // Strip the prefix/postfix and return 'Some' to indicate success
    let len = input.Length - prefix.Length - postfix.Length
    Some(input.Substring(prefix.Length, len))
  else None // Return 'None' - string doesn't match the pattern

Теперь мы можем использовать Middle в сопоставлении с образцом (например,при использовании match):

match "[aaa]"  with
| Middle "[" "]" mid -> mid
| all -> all
4 голосов
/ 20 сентября 2010

Параметризованный Активные паттерны на помощь!

let (|HasPrefixSuffix|_|) (pre:string, suf:string) (s:string) =
    if s.StartsWith(pre) then
        let rest = s.Substring(pre.Length)
        if rest.EndsWith(suf) then
            Some(rest.Substring(0, rest.Length - suf.Length))
        else
            None
    else
        None

let Test s =
    match s with
    | HasPrefixSuffix("\"","\"") inside -> 
        printfn "quoted, inside is: %s" inside
    | _ -> printfn "not quoted: %s" s

Test "\"Wow!\""
Test "boring"
3 голосов
/ 20 сентября 2010

… или просто используйте простое старое регулярное выражение

let Middle input =
    let capture = Regex.Match(input, "\"([^\"]+)\"")
    match capture.Groups.Count with
    | 2 -> capture.Groups.[1].Value
    | _ -> input
2 голосов
/ 20 сентября 2010

Шаблоны имеют ограниченную грамматику - вы не можете просто использовать любое выражение. В этом случае я бы просто использовал if / then / else:

let middle (s:string) =
  if s.[0] = '"' && s.[s.Length - 1] = '"' && s.Length >= 2 then 
    s.Substring(1,s.Length - 2)
  else s

Если вы часто делаете захват середины строки со статически известными началами и окончаниями, вы всегда можете использовать активный шаблон, как предполагает Томас.

0 голосов
/ 21 сентября 2010

Не уверен, насколько это эффективно:

let GetQuote (s:String) (q:char) = 
        s 
        |> Seq.skip ((s |> Seq.findIndex (fun c -> c = q))+1)
        |> Seq.takeWhile (fun c-> c <> q) 
        |> Seq.fold(fun acc c -> String.Format("{0}{1}", acc, c)) ""

Или вот с подстрокой вместо сгиба:

let GetQuote2 (s:String) (q:char)  = 
    let isQuote = (fun c -> c = q)
    let a = (s |> Seq.findIndex isQuote)+1
    let b = ((s |> Seq.take(a) |> Seq.findIndex isQuote)-1)
    s.Substring(a,b);

Они получат первый экземпляр цитируемого текста в любом месте строки, например, «Hello [World]» -> «World»

...