运行以下代码时
do line <- getLine
putStrLn line
或
getLine >>= putStrLn
然后,在
之后 getLine >>= putStrLn
输入
µ
一个遇到此输出:
⠀
现在,我已经预先尝试过chcp 65001
,但是该操作不起作用,并且stdin
的编码为utf8
。
没有putStrLn
的检查显示:
getLine
µ
'\NIL'
我的环境:
Windows 10版本10.0.17134内部版本17134
联想IdeaPad 510-15IKB
BIOS版本LENOVO 3JCN30WW
GHCi v 8.2.2
如何解决?
编辑:具体来说,以下操作序列会导致这种情况:
cmd
chcp 65001
ghci
getLine >>= putStrLn
µ
但是,以下内容却没有:
ghci
ghci.exe
处打开%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(德语)。
答案 0 :(得分:4)
控制台输入/输出在Windows上完全损坏,并且已经有一段时间了。这是跟踪与Windows上的IO相关的所有问题的最重要的问题: https://ghc.haskell.org/trac/ghc/ticket/11394
我相信,这两张票最能说明您所遇到的行为:
目前唯一的解决方法是手动使用Windows API来处理控制台输出/输入,这很麻烦。
编辑
所以,为了它的地狱,我决定忍受其中的一些痛苦。 :)
这是下面代码的输出:
0 1 3
939 2074 Die CSU verlangt von der schwarz-gelben Koalit... 1.0
这绝不是一个完全正确或安全的解决方案,但它确实有效:
====
Input: µ
Output: µ
====
编辑2
@dfeuer要求我列出该答案中不安全,不正确或不完整的内容。我只在Linux上真正编写过代码,所以我不是Windows程序员,但是在我的想法浮现在脑海的是,在将这些代码用于实际程序之前,需要对这些事情进行更改:
GetConsoleMode
API调用来确定。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"
来检查呼叫是否成功,以及何时不使用GetLastError
来将错误报告给用户。BOOL
只能处理hwGetStrN
个字符,因此需要递归调用才能获得类似于n
的行为hGetLine
是DWORD
,因此Word32
调用容易受到整数溢出的影响,这既不正确也不安全。fromIntegral len
,而stdcall
为ccall
,因此需要一些CPP