2010-10-31 47 views
23

sau Chương trình Haskell nhắc nhở người dùng cho một mật khẩu trong nhà ga và tiếp tục nếu anh ta đã bước vào đúng một:nhắc nhập mật khẩu trong Haskell dòng lệnh ứng dụng

main = do 
    putStrLn "Password:" 
    password <- getLine 

    case hash password `member` database of 
     False -> putStrLn "Unauthorized use!" 
     True -> do 
       ... 

Thật không may, mật khẩu sẽ xuất hiện trên màn hình khi người dùng gõ nó, mà tôi muốn tránh.

Làm cách nào để đọc chuỗi ký tự mà người dùng nhập mà không hiển thị trên màn hình? Tương đương với getLine cho mục đích này là gì?

Tôi đang sử dụng MacOS X, nhưng tôi cũng muốn điều này cũng hoạt động trên Windows và Linux.

+1

Sử dụng [haskeline] (http://hackage.haskell.org/packages/archive/haskeline/0.6.3.1/doc/html/System -Console-Haskeline.html # v: getPassword). –

+1

@TomMd: Nhận xét của bạn là câu trả lời phù hợp cho câu hỏi. Tại sao không làm cho nó một câu trả lời thực sự để nó có thể được bình chọn trên, bình luận về, chấp nhận, và cetera? –

Trả lời

32

Hãy thử tiếp theo:

module Main 
where 

import System.IO 
import Control.Exception 

main :: IO() 
main = getPassword >>= putStrLn . ("Entered: " ++) 

getPassword :: IO String 
getPassword = do 
    putStr "Password: " 
    hFlush stdout 
    pass <- withEcho False getLine 
    putChar '\n' 
    return pass 

withEcho :: Bool -> IO a -> IO a 
withEcho echo action = do 
    old <- hGetEcho stdin 
    bracket_ (hSetEcho stdin echo) (hSetEcho stdin old) action 
+3

Làm việc giống như một sự quyến rũ! Bạn có thể biến điều này thành một ví dụ 'getPassword :: IO String' hoàn chỉnh nhỏ, vì vậy tôi có thể làm cho câu trả lời này được chấp nhận không? –

+0

Tôi đã chỉnh sửa đoạn mã – Yuras

+0

Tuyệt vời, cảm ơn bạn! –

9

Có thể vô hiệu hóa lặp lại trong thiết bị đầu cuối với the System.Posix.Terminal module. Tuy nhiên, điều này đòi hỏi hỗ trợ POSIX, vì vậy có thể không hoạt động trên Windows (tôi đã không kiểm tra).

import System.Posix.Terminal 
import System.Posix.IO (stdInput) 

getPassword :: IO String 
getPassword = do 
    tc <- getTerminalAttributes stdInput 
    setTerminalAttributes stdInput (withoutMode tc EnableEcho) Immediately 
    password <- getLine 
    setTerminalAttributes stdInput tc Immediately 
    return password 

main = do 
    putStrLn "Password:" 
    password <- getPassword 
    putStrLn "Name:" 
    name <- getLine 
    putStrLn $ "Your password is " ++ password ++ " and your name is " ++ name 

Lưu ý rằng stdin là dòng đệm, vì vậy nếu bạn sử dụng putStr "Password:" thay vì putStrLn, bạn cần phải tuôn ra bộ đệm đầu tiên, nếu không kịp thời sẽ bị ức chế cũng có.

+1

Đẹp, có một cuộc nổi dậy! Có vẻ như 'hSetEcho' khiêm tốn từ 'System.IO' sẽ thực hiện thủ thuật. :-) –

+0

@Heinrich: Ah. Quá khiêm tốn để được chú ý: P – kennytm

13

Có một getPassword trong System.Console.Haskeline. Có lẽ nó là một overkill cho trường hợp của bạn nhưng ai đó có thể tìm thấy nó hữu ích.

Một ví dụ:

> runInputT defaultSettings $ do {p <- getPassword (Just '*') "pass:"; outputStrLn $ fromJust p} 
pass:*** 
asd 
+1

Tuyệt vời! Tôi không biết về thư viện 'haskeline'. –

+1

Bất kỳ ý tưởng nào tại sao tất cả các hàm Haskeline trả lại một chuỗi «Có thể' thay vì một chuỗi'? Không có tài liệu về nó, và có vẻ như với tôi rằng nó phải luôn luôn trả về 'String', giống như tiêu chuẩn' getLine'. – jameshfisher

+0

Đó là vì tiêu chuẩn 'getLine' là một phần chức năng, mà sẽ tăng ngoại lệ khi nhận được một' EOT'. haskeline thay vào đó sẽ trả về 'Nothing', cho phép bạn làm điều gì đó khác khi bạn có một dòng trống, so với'^D' – berdario

4

Như tôi đã nhận xét ở trên, tôi đề nghị bạn sử dụng haskeline, mà là một thư viện kịp thời đầy đủ. Tôi đã sử dụng nó một cách vui vẻ cho LambdaCalculator mà không có khiếu nại.

+0

Bất kỳ ý tưởng nào tại sao tất cả các hàm Haskeline trả về một 'chuỗi có thể' thay vì' chuỗi'? Không có tài liệu về nó, và có vẻ như với tôi rằng nó phải luôn luôn trả về 'String', giống như tiêu chuẩn' getLine'. – jameshfisher

+0

@jameshfisher Nó không phải là ngay lập tức rõ ràng với tôi, không có. Nó sẽ mất một số đào trong nguồn. –

5

withEcho có thể được viết với một chút ít noice:

withEcho :: Bool -> IO a -> IO a 
withEcho echo action = 
    bracket (hGetEcho stdin) 
      (hSetEcho stdin) 
      (const $ hSetEcho stdin echo >> action) 
+1

Tôi nghĩ rằng đây là một chút quá thông minh đối với tôi, tôi phải nhìn khá khó để tìm ra cách thức hoạt động này. Một sửa chữa tốt hơn có thể là yếu tố ra hành vi 'chạy hành động với cài đặt tạm thời thay đổi' thành một cái gì đó như 'withTemp :: a -> IO a -> (a -> IO b) -> IO c -> IO c',' withTemp tempValue getter setter action = ... 'để bạn có thể định nghĩa' withEcho echo = withTemp echo (hGetEcho stdin) (hSetEcho stdin) '. –

Các vấn đề liên quan