资料来源:格雷厄姆·赫顿。“ Haskell 编程”(第 180 页)
\n\n\n\n
\n- 使用
\ngetCh,定义一个readLine :: IO String与 getLine 行为方式相同的操作,不同之处在于它还允许使用删除键删除字符。
\n\n提示:删除字符为
\n\xe2\x80\x99\\ DEL\xe2\x80\x99,光标后移一格的控制字符为\xe2\x80\x99\\b\xe2\x80\x99。
我使用一个\'\\b\'角色解决了这个练习,但在网上发现解算器使用了两个角色。为什么这个问题的解决者使用"\\b \\b"而不是"\\b"?似乎是一个错字,但我不确定。我发现它适用于三个\'\\b\'角色。
这个角色是如何运作的?
\nimport System.IO\n\ngetCh :: IO Char\ngetCh = do\n hSetEcho stdin False\n x <- getChar\n hSetEcho stdin True\n return x\n\nreadLine :: IO String\nreadLine = readLine\' ""\n\nreadLine\' :: String -> IO String\nreadLine\' xs = do\n x <- getCh\n case x of\n \'\\n\' -> do\n putChar \'\\n\'\n return xs\n \'\\DEL\' ->\n if null xs\n then readLine\' ""\n else do\n putStr "\\b \\b"\n readLine\' (init xs)\n _ -> do\n putChar x\n readLine\' (xs ++ [x])\nRun Code Online (Sandbox Code Playgroud)\n
如果您仅使用"\\b",光标将移至左侧,但不会删除那里看到的字符,直到您用新的按键输入覆盖它。例如,FOO\xe2\x86\x90最终你会得到fo\xe2\x81\x81o,但这会产生误导:如果你没有按另一个字母键而是立即按下\xe2\x86\xb5,那么结果看起来仍然是foo,而实际上它只是fo。
为了避免这种情况,解决方案"\\b \\b"向左移动,用空格覆盖字符以可视化删除它,然后立即再次向左移动。这与向左移动一次并就地删除字符具有相同的效果。
\b将光标向后移动一个字符,但不会删除它(至少在大多数终端上不会)。例如,字符串abcde\b将显示为abcde,字符串将显示abcde\bf为abcdf。这就是为什么序列显式地\b \b用空格覆盖最后一个字符,然后再次将光标移回。