首页 > 解决方案 > 有没有办法读取 F# 控制台应用程序中特定行上的文本?

问题描述

我想将某行中的文本放入变量中。我能以某种方式使用 Console.CursorLeft 和 Console.CursorTop 来做到这一点吗?程序甚至有可能知道光标当前在哪个字母上?我有一个程序,您可以在其中使用箭头键突出显示某行,但它要求您已经知道该行的内容并重复它。

这是到目前为止的程序,它可以让您了解我正在尝试做的事情。

open System

Console.BackgroundColor <- ConsoleColor.White
Console.ForegroundColor <- ConsoleColor.Black
printfn "OPTION 1"
Console.BackgroundColor <- ConsoleColor.Black
Console.ForegroundColor <- ConsoleColor.White
printfn "Option 2"
printfn "Option 3"

Console.SetCursorPosition(0, 0)

let mutable exit = false

while not exit do
    let mutable key = Console.ReadKey()
    if key.Key.Equals(ConsoleKey.UpArrow) then
        Console.SetCursorPosition(0, Console.CursorTop - 1)
    if key.Key.Equals(ConsoleKey.DownArrow) then
        Console.SetCursorPosition(0, Console.CursorTop + 1)
    if key.Key.Equals(ConsoleKey.Enter) then
        let selected = Console.CursorTop + 1
        Console.SetCursorPosition(0, 3)
        printfn "You selected %i" (selected)
        exit <- true
    let mutable test = Console.CursorTop
    Console.SetCursorPosition(0, 0)
    printfn "Option 1"
    printfn "Option 2"
    printfn "Option 3"
    if test = 0 then
        Console.SetCursorPosition(0, 0)
        Console.BackgroundColor <- ConsoleColor.White
        Console.ForegroundColor <- ConsoleColor.Black
        printfn "OPTION 1"
    if test = 1 then
        Console.SetCursorPosition(0, 1)
        Console.BackgroundColor <- ConsoleColor.White
        Console.ForegroundColor <- ConsoleColor.Black
        printfn "OPTION 2"
    if test = 2 then
        Console.SetCursorPosition(0, 2)
        Console.BackgroundColor <- ConsoleColor.White
        Console.ForegroundColor <- ConsoleColor.Black
        printfn "OPTION 3"
    Console.BackgroundColor <- ConsoleColor.Black
    Console.ForegroundColor <- ConsoleColor.White
    Console.SetCursorPosition(0, test)

Console.Read() |> ignore

标签: .netf#

解决方案


鉴于评论中的澄清,我认为绝对没有理由以需要阅读打印到控制台的文本的方式编写应用程序。

如果你这样做了,你将基本上依赖控制台文本来维护应用程序的状态。换句话说,您将状态保持在有限大小的 2D 字符数组中!

更好的方法是使用 F# 类型来表示状态。例如,您可以定义一个记录来保存您的菜单选项、选择以及是否已进行选择的标志:

type State = 
  { Options : string list
    Selection : int
    Completed : bool }

现在您可以定义一个打印状态的函数:

let printMenu menu = 
  Console.SetCursorPosition(0, 0)
  for i, option in Seq.indexed menu.Options do
    if i = menu.Selection then
      Console.BackgroundColor <- ConsoleColor.White
      Console.ForegroundColor <- ConsoleColor.Black
    printfn "%s" option
    Console.BackgroundColor <- ConsoleColor.Black
    Console.ForegroundColor <- ConsoleColor.Gray

还有一个在按下键时计算新状态的函数:

let handleKey (key:ConsoleKeyInfo) state = 
  match key.Key with
  | ConsoleKey.UpArrow -> { state with Selection = state.Selection-1 }
  | ConsoleKey.DownArrow -> { state with Selection = state.Selection+1 }
  | ConsoleKey.Enter -> { state with Completed = true }
  | _ -> state

最后,您可以使用菜单选项定义初始状态。我会保留你的while循环,所以这将是一个可变变量:

let mutable state = 
  { Options = ["Option 1"; "Option 2"; "Option 3"] 
    Completed = false
    Selection = 0 }      

主要的应用程序逻辑现在超级简单。只需在state.Completed为 false 时循环,打印菜单,读取密钥并更新状态:

while not state.Completed do
  printMenu state
  let key = Console.ReadKey()
  state <- handleKey key state

Console.SetCursorPosition(0, 3)
printfn "You selected %i" state.Selection

这种构建应用程序的方式更加清晰,因为它将状态保持在显式数据类型中,并且与对其进行操作的各种函数分开。


推荐阅读