【发布时间】:2011-04-14 18:20:43
【问题描述】:
如果被引用,我想使用 FSharp 提取字符串的中间部分,类似这样:
let middle =
match original with
| "\"" + mid + "\"" -> mid
| all -> all
但由于模式表达式中的中缀运算符+,它不起作用。我怎样才能提取这个?
【问题讨论】:
标签: string f# pattern-matching
如果被引用,我想使用 FSharp 提取字符串的中间部分,类似这样:
let middle =
match original with
| "\"" + mid + "\"" -> mid
| all -> all
但由于模式表达式中的中缀运算符+,它不起作用。我怎样才能提取这个?
【问题讨论】:
标签: string f# pattern-matching
我不认为对此有任何直接支持,但您当然可以编写一个主动模式。活动模式允许您实现自己的代码,这些代码将作为模式匹配的一部分运行,并且您可以提取和返回部分值。
以下是一个模式,它采用两个参数(前缀和后缀字符串),如果给定的输入以指定的字符串开始/结束,则成功。模式不完整(可能失败),所以我们将使用|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),但很高兴看到我们有相同的想法! :)
参数化 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"
【讨论】:
... 或者只是使用普通的旧正则表达式
let Middle input =
let capture = Regex.Match(input, "\"([^\"]+)\"")
match capture.Groups.Count with
| 2 -> capture.Groups.[1].Value
| _ -> input
【讨论】:
模式有一个有限的语法 - 你不能只使用任何表达式。在这种情况下,我只使用 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 的建议使用主动模式。
【讨论】:
不确定效率如何:
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”
【讨论】: