【问题标题】:How can I extract the middle part of a string in FSharp?如何在 FSharp 中提取字符串的中间部分?
【发布时间】:2011-04-14 18:20:43
【问题描述】:

如果被引用,我想使用 FSharp 提取字符串的中间部分,类似这样:

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

但由于模式表达式中的中缀运算符+,它不起作用。我怎样才能提取这个?

【问题讨论】:

标签: string f# pattern-matching


【解决方案1】:

我不认为对此有任何直接支持,但您当然可以编写一个主动模式。活动模式允许您实现自己的代码,这些代码将作为模式匹配的一部分运行,并且您可以提取和返回部分值。

以下是一个模式,它采用两个参数(前缀和后缀字符串),如果给定的输入以指定的字符串开始/结束,则成功。模式不完整(可能失败),所以我们将使用|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

【讨论】:

  • 你的实施策略比我的好(只打了一次Substring),但很高兴看到我们有相同的想法! :)
  • 谢谢托马斯。你的回答很清楚也很有帮助。还要感谢 brian 和 kvb。不幸的是,我是 StackOverflow 的新手,没有足够的积分来回答您的问题。
  • @Brian:是的,看起来我们发布了几乎相同的答案!我还考虑了您的策略(但认为检查长度更容易)并且我还考虑使用元组传递两个参数:-)
【解决方案2】:

参数化 active patterns 来救援!

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】:

... 或者只是使用普通的旧正则表达式

let Middle input =
    let capture = Regex.Match(input, "\"([^\"]+)\"")
    match capture.Groups.Count with
    | 2 -> capture.Groups.[1].Value
    | _ -> input

【讨论】:

  • 返回一个选项类型可能会更好?
  • 作者想要返回整个字符串,如果它没有被引用,我理解这个问题。
【解决方案4】:

模式有一个有限的语法 - 你不能只使用任何表达式。在这种情况下,我只使用 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

如果您会经常使用静态已知的开头和结尾来抓取字符串的中间部分,那么您始终可以按照 Tomas 的建议使用主动模式。

【讨论】:

  • 感谢 kvb。我更喜欢 Tomas 的回答,只是因为我不想使用命令式方式。
【解决方案5】:

不确定效率如何:

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)) ""

或者用 Substring 代替折叠:

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”

【讨论】:

    猜你喜欢
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-20
    • 2021-08-26
    • 1970-01-01
    相关资源
    最近更新 更多