search
LoginSignup
0

posted at

【C#】コンソールアプリで入力された文字を非表示にしてReadLineする

動機:パスワードをユーザーに入力させたい

パスワードとかをユーザーに入力させたいときに、System.Console.ReadLineメソッドだと画面にそのまま表示されてしまいます。
画面の目の前にいる人には見せても別にオッケーだよ!って考え方もあるようですが、個人的には隠したいのです。
標準のSystem.Consoleクラスにはどうもそういう機能は無いようです。無いんだ…そっかぁ…
Console.ReadPasswordメソッドとかがあればいいのに…
誰かが気の利いたライブラリを作ってて、私が知らないだけで実はデファクトスタンダードなんですなんてことがあるかもしれませんが…
ともかく、大したコードにはならなさそうなので、参考文献をコピペしながらちょっと自分好みに手を加えてみることにします。

参考文献

ソースの概要

  • 基本的には参考文献のとおりのコードだよ
  • エコーバック無しでReadLineするメソッドNonvisibleReadline()を作ってみたよ
  • 好きな文字でシーリングできるオーバーロードNonvisibleReadline(char SealingChar)も作ってみたよ
    • シーリング文字に全角文字を指定すると、バックスペースを入力したときに文字の半分だけ消えるバグがあるけど、面倒くさいから放置したよ
  • 非同期で実行して、外部からキャンセルできるようにもしてみたよ
  • 大したことはやっていないので、適当に煮るなり焼くなりしてね

ソース

using System;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleUtilsLib
{
    public static class ConsoleUtils
    {
        /// <summary>
        /// エコーバック無しのReadLine
        /// </summary>
        /// <returns>入力された文字列を返す。ESCが押された場合はその時点でnullを返す。</returns>
        /// <exception cref="InvalidOperationException"/>
        /// <exception cref="IOException"/>
        public static string? NonvisibleReadline()
        {
            return NonvisibleReadline(new CancellationToken());
        }
        /// <summary>
        /// エコーバック無しのReadLine
        /// </summary>
        /// <returns>入力された文字列を返す。ESCが押された場合はその時点でnullを返す。</returns>
        /// <param name="SealingChar">隠すときの文字</param>
        /// <exception cref="InvalidOperationException"/>
        /// <exception cref="IOException"/>
        public static string? NonvisibleReadline(char SealingChar)
        {
            return NonvisibleReadline(new CancellationToken(), SealingChar);
        }
        private static string? NonvisibleReadline(CancellationToken cancellationToken, char? SealingChar = null)
        {
            // 入力された文字を格納するためのバッファを用意
            var password = new StringBuilder();

            while (!cancellationToken.IsCancellationRequested)
            {
                // キー入力が来るまでなにもしない
                if (!Console.KeyAvailable)
                    continue;
                // 入力されたキー情報を読む(エコーバックはしない)
                var keyinfo = Console.ReadKey(true);

                switch (keyinfo.Key)
                {
                    case ConsoleKey.Escape:
                        // ESCキーが押された場合は、入力がキャンセルされたものとしてnullを返す
                        Console.WriteLine();
                        return null;

                    case ConsoleKey.Enter:
                        // Enterキーが押された場合は、入力が確定されたものとしてバッファに格納した文字列を返す
                        Console.WriteLine();
                        return password.ToString();

                    case ConsoleKey.Backspace:
                        // BackSpaceキーが押された場合は、バッファから最後の一文字を削除する
                        if (0 < password.Length)
                        {
                            password.Length -= 1;
                            if (SealingChar is not null) Console.Write("\b \b"); // SealingCharがあるときは最後の1文字をスペースで消す
                        }
                        else
                            // 削除できる文字がない場合は、ビープ音を鳴らす
                            Console.Beep();
                        break;

                    default:
                        if (Char.IsControl(keyinfo.KeyChar)) // コントロールキーやファンクションキーが入力された場合は、ビープ音を鳴らす
                            Console.Beep();
                        else
                        {
                            password.Append(keyinfo.KeyChar);
                            Console.Write(SealingChar);
                        }
                        break;
                }
            }
            return null; // トークンでキャンセルされたときもnullを返す
        }
        public static Task<string?> NonvisibleReadlineAsync()
        {
            return NonvisibleReadlineAsync(new CancellationToken(), null);
        }
        public static Task<string?> NonvisibleReadlineAsync(char SealingChar)
        {
            return NonvisibleReadlineAsync(new CancellationToken(), SealingChar);
        }
        public static Task<string?> NonvisibleReadlineAsync(CancellationToken cancellationToken)
        {
            return NonvisibleReadlineAsync(cancellationToken, null);
        }
        public static Task<string?> NonvisibleReadlineAsync(CancellationToken cancellationToken, char SealingChar)
        {
            return NonvisibleReadlineAsync(cancellationToken, (char?)SealingChar);
        }
        private static Task<string?> NonvisibleReadlineAsync(CancellationToken cancellationToken, char? SealingChar)
        {
            return Task.Run(() => NonvisibleReadline(cancellationToken, SealingChar));
        }
    }
}

感想

  • 参考文献の時代とは違って、keyinfo.KeyCharShiftCaps Lockを考慮されて入力すべき文字が入っているようで、便利になったなあ…とか思いました。
  • BeepのON,OFFも指定できるようにしてもいいかもなあとか。
  • オーバーロードで書いちゃったけど、省略可能引数でやっても良かったなあ

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0