运行以下代码时
do line <- getLine
putStrLn line
或,
getLine >>= putStrLn
然后,之后
getLine >>= putStrLn
入场
µ
我们会遇到这样的输出:
⠀
现在,我已经预先尝试过chcp 65001
了,它不起作用,而stdin
的编码是utf8
。
没有putStrLn
的考试显示:
getLine
µ
'\NIL'
我的环境:
Windows 10版本10.0.17134构建17134
联想ideapad 510-15 510
BIOS版联想3JCN30WW
GHCi诉8.2.2
如何解决这一问题?
编辑:具体来说,以下操作顺序导致了这种情况:
cmd
chcp 65001
型ghci
型getLine >>= putStrLn
型µ
型然而,以下并不是:
ghci
ghci.exe
at %PROGRAMS%\Haskell Platform\8.2.2\bin
注意:%PROGRAMS%
不是真实的环境变量。
编辑:根据请求,GHC.IO.Encoding.getLocaleEncoding
的输出
UTF-8
此外,System.IO.hGetEncoding stdin
的输出
Just UTF-8
(使用chcp 65001
时)
编辑:字符是U+00B5。我使用德国键盘,系统本地化德国,语言设置英语,键盘语言ENG与德语布局。
发布于 2018-08-05 02:14:10
控制台输入/输出在Windows上完全崩溃,而且已经有一段时间了。下面是跟踪Windows上与IO相关的所有问题的顶级票证:https://ghc.haskell.org/trac/ghc/ticket/11394
我相信,这两张票最能描述你正在经历的行为:
现在唯一的工作就是手动使用Windows来处理控制台输出/输入,这本身就是一个痛苦。
编辑
所以,我决定忍受一些痛苦。:)
下面是代码的输出:
====
Input: µ
Output: µ
====
这绝不是一个完全正确或安全的解决方案,但它确实有效:
module Main where
import Control.Monad
import System.IO
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.C.String
import Foreign.C.Types
import Foreign.Storable
import System.Win32
import System.Win32.Types
import Graphics.Win32.Misc
foreign import ccall unsafe "windows.h WriteConsoleW"
c_WriteConsoleW :: HANDLE -> LPWSTR -> DWORD -> LPDWORD -> LPVOID -> IO BOOL
foreign import ccall unsafe "windows.h ReadConsoleW"
c_ReadConsoleW :: HANDLE -> LPWSTR -> DWORD -> LPDWORD -> LPVOID -> IO BOOL
-- | Read n characters from a handle, which should be a console stdin
hwGetStrN :: Int -> Handle -> IO String
hwGetStrN maxLen hdl = do
withCWStringLen (Prelude.replicate maxLen '\NUL') $ \(cstr, len) -> do
lpNumberOfCharsWrittenForeignPtr <- mallocForeignPtr
withHandleToHANDLE hdl $ \winHANDLE ->
withForeignPtr lpNumberOfCharsWrittenForeignPtr $ \lpNumberOfCharsRead -> do
c_ReadConsoleW winHANDLE cstr (fromIntegral len) lpNumberOfCharsRead nullPtr
numWritten <- peek lpNumberOfCharsRead
peekCWStringLen (cstr, fromIntegral numWritten)
-- | Write a string to a handle, which should be a console stdout or stderr.
hwPutStr :: Handle -> String -> IO ()
hwPutStr hdl str = do
void $ withCWStringLen str $ \(cstr, len) -> do
lpNumberOfCharsWrittenForeignPtr <- mallocForeignPtr
withHandleToHANDLE hdl $ \winHANDLE ->
withForeignPtr lpNumberOfCharsWrittenForeignPtr $ \ lpNumberOfCharsWritten ->
c_WriteConsoleW winHANDLE cstr (fromIntegral len) lpNumberOfCharsWritten nullPtr
main :: IO ()
main = do
hwPutStr stdout "====\nInput: "
str <- hwGetStrN 10 stdin
hwPutStr stdout "Output: "
hwPutStr stdout str
hwPutStr stdout "====\n"
编辑2
@dfeuer要求我列出不安全、不正确或不完整的问题,并给出答案。我只在Linux上编写代码,所以我不是Windows程序员,但在真正的程序中使用这些代码之前,我会突然想到一些需要改变的东西:
GetConsoleMode
API调用来确定。BOOL
来检查调用是否成功,并在不使用GetLastError
向用户报告错误时进行检查。hwGetStrN
只能处理n
字符,因此需要递归调用才能获得类似于hGetLine
的行为。DWORD
是Word32
,因此fromIntegral len
调用容易受到整数溢出的影响,这既不正确又不安全。stdcall
,而ccall
则是x86_64
,所以有些CPP是必需的。https://stackoverflow.com/questions/51661192
复制