2010-09-20 2 views
5

tôi muốn trích xuất các phần giữa của một chuỗi sử dụng FSharp nếu nó được trích dẫn, tương tự như thế này:Làm thế nào tôi có thể trích xuất phần giữa của một chuỗi trong FSharp?

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

Nhưng nó không hoạt động vì sự điều hành ghi vào + trong biểu mẫu. Làm thế nào tôi có thể trích xuất này?

+0

Xem thêm http://stackoverflow.com/questions/3722591/pattern-matching-on-the-beginning- of-a-string-in-f – Brian

Trả lời

10

Tôi không nghĩ có bất kỳ hỗ trợ trực tiếp nào cho điều này, nhưng bạn chắc chắn có thể viết mẫu đang hoạt động. Các mẫu hoạt động cho phép bạn triển khai mã của riêng bạn sẽ chạy như một phần của mẫu phù hợp và bạn có thể giải nén & trả về một phần của giá trị.

Sau đây là mẫu lấy hai tham số (tiền tố và chuỗi postfix) và thành công nếu đầu vào đã cho bắt đầu/kết thúc bằng chuỗi được chỉ định. Mô hình là không đầy đủ (có thể thất bại), vì vậy chúng tôi sẽ sử dụng cú pháp |Name|_| và nó sẽ cần phải trả lại giá trị tùy chọn:

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 

Bây giờ chúng ta có thể sử dụng Middle trong mô hình kết hợp (ví dụ như khi sử dụng match):

match "[aaa]" with 
| Middle "[" "]" mid -> mid 
| all -> all 
+0

Chiến lược triển khai của bạn tốt hơn so với tôi (chỉ có một cuộc gọi đến 'Chuỗi con'), nhưng vui mừng khi thấy chúng tôi có cùng ý tưởng! :) – Brian

+0

Cảm ơn Tomas. Câu trả lời của bạn rất rõ ràng và hữu ích. Nhờ brian và kvb cũng. Thật không may, tôi mới trong StackOverflow và không có đủ tín dụng để đưa ra câu trả lời của bạn. – newwave

+0

@Brian: Có, có vẻ như chúng tôi đã đăng gần như cùng một câu trả lời! Tôi cũng coi chiến lược của bạn (nhưng nghĩ rằng chiều dài kiểm tra là dễ dàng hơn) và tôi cũng xem xét việc vượt qua hai đối số bằng cách sử dụng một tuple :-) –

2

Mẫu có ngữ pháp hạn chế - bạn không thể chỉ sử dụng bất kỳ biểu thức nào. Trong trường hợp này, tôi muốn chỉ cần sử dụng một nếu/rồi/khác:

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

Nếu grabbing giữa một chuỗi với phần đầu và cuối tĩnh được biết đến là một cái gì đó mà bạn sẽ làm được rất nhiều, sau đó bạn có thể luôn sử dụng một mẫu hoạt động như Tomas gợi ý.

+0

Cảm ơn kvb. Tôi thích câu trả lời của Tomas chỉ vì tôi không muốn sử dụng cách bắt buộc. – newwave

3

Tham số active patterns để giải cứu!

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" 
+1

Các mẫu hoạt động tham số được kích hoạt đáng kinh ngạc. – gradbot

+0

Nghiêm túc! :-) –

2

... hoặc chỉ sử dụng đồng bằng cũ biểu thức chính quy

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

Có thể tốt hơn để trả lại một loại tùy chọn? –

+0

Tác giả muốn trả lại toàn bộ chuỗi nếu nó không được trích dẫn, khi tôi hiểu câu hỏi. –

0

Không chắc thế nào hiệu quả này là:

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

Hoặc có này với xâu ở vị trí của lần:

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

Đây sẽ nhận được ví dụ đầu tiên của văn bản được trích dẫn ở bất kỳ đâu trong chuỗi, ví dụ: "Xin chào [Thế giới]" -> "Thế giới"