解析传入的Ascii字符TCP流,处理退格字符

Rog*_*son 7 f# ascii telnet

我需要从套接字解析输入流.数据是从Telnet客户端发送的,因此我想通过查找'\r'流中的第一个字符来处理传入的字符串,然后在返回char之前选择字节并最终处理任何backspace '\b'字符.

'\b'在这里处理比特的惯用方法是什么?我目前正在使用一个可变堆栈并将chars推到它上面,如果有一个退格,我会弹出最后一个char.然后将结果转换为字符串.

但我认为使用模式匹配和尾递归可能有一些很好的方法.那么,如何以F#方式完成呢?

let receiveInput (inputBuffer:StringBuilder) (received:Tcp.Received)=
    let text = Encoding.ASCII.GetString(received.Data.ToArray());
    inputBuffer.Append(text) |> ignore

    let all = inputBuffer.ToString()
    match all.IndexOf('\r') with
    | enter when enter >= 0 ->
        let textToProcess = all.Substring(0,enter)
        inputBuffer.Remove(0,enter+2) |> ignore

        //this is the part I'm wondering about
        let stack = new Stack<char>()
        for c in textToProcess do
            if c = '\b' then stack.Pop() |> ignore
            else stack.Push c

        let input = new System.String(stack |> Seq.rev |> Seq.toArray)

        Some(input)
    | _ ->
        None
Run Code Online (Sandbox Code Playgroud)

Mar*_*ann 12

让我们首先将有问题的部分隔离到一个函数:

open System
open System.Collections.Generic

let handleBackspaces textToProcess : string =
    let stack = Stack<char>()
    for c in textToProcess do
        if c = '\b' then stack.Pop() |> ignore
        else stack.Push c
    stack |> Seq.rev |> Seq.toArray |> String
Run Code Online (Sandbox Code Playgroud)

这有一个可变变量(stack).只要有变量变量,就可以在递归函数中用累加器值替换它.这是一种方法:

open System

let handleBackspaces' textToProcess : string =
    let rec imp acc = function
        | [] -> acc
        | '\b'::cs -> imp (acc |> List.tail) cs
        | c::cs -> imp (c::acc) cs
    textToProcess |> Seq.toList |> imp [] |> List.rev |> List.toArray |> String
Run Code Online (Sandbox Code Playgroud)

你会注意到我已经调用了累加器值acc.该imp函数具有类型char list -> char list -> char list,并且它与传入匹配char list:如果它为空,则返回累加器; 如果它具有'\b'头部,则char通过使用将其从累加器中移除List.tail; 在所有其他情况下,它是第一个char到累加器并递归调用自身.

这是一个(希望令人满意的)FSI会议:

> handleBackspaces' "b\bfoo";;
val it : string = "foo"
> handleBackspaces' "foo";;
val it : string = "foo"
> handleBackspaces' "bar\bz";;
val it : string = "baz"
> handleBackspaces' "bar\b\boo";;
val it : string = "boo"
> handleBackspaces' "b\bfa\boo";;
val it : string = "foo"
Run Code Online (Sandbox Code Playgroud)

一旦人们理解了如何将某事物建模为递归函数,就应该可以使用折叠来实现它,正如Ryan W Gough指出的那样.这是一种方法:

let handleBackspaces'' textToProcess : string =
    textToProcess
    |> Seq.fold (fun acc c -> if c = '\b' then acc |> List.tail else c::acc) []
    |> List.rev
    |> List.toArray
    |> String
Run Code Online (Sandbox Code Playgroud)

  • @RyanWGough`reduce`类似于`fold`,但是当输入为空时它会崩溃. (3认同)