コンソールアプリケーションでユーザーから文字列を入力させる基本は、Console.ReadLine
を正しく理解し、適切にバリデーションや変換を行うことです。
本記事では、Console.ReadLine
の戻り値やブロッキングの挙動から、空行の扱い、数値・日付変換、複数行入力の終了条件まで、実用的なサンプルコードとともに詳しく解説します。
Console.ReadLineとは?
Console.ReadLine
は、標準入力から1行の文字列を読み取るための最も基本的なAPIです。
ユーザーがEnterキーを押すまで入力を待ち、行末の改行は返す文字列に含まれません。
Console.ReadLineの概要と戻り値(string?)
- 戻り値の型は
string?
です。これは、文字列が返る場合とnull
が返る場合があることを意味します。 - 標準入力が終端(EOF)に達した場合や入力がリダイレクトされていて末尾に達した場合、
null
が返ります。 - 通常の対話入力では、ユーザーがEnterを押すと、改行を除いた文字列が返ります。
典型的にnull
になるケース
- Windows: コンソールでCtrl+Zの後にEnterを押す
- macOS/Linux: Ctrl+Dを押す
- 入力をファイルやパイプから受け取り、その終端に達した場合
改行までの読み取りとブロッキングの挙動
Console.ReadLine
はブロッキング呼び出しです。
ユーザーが改行(Enter)を送るまで、呼び出し元スレッドは待機します。
これにより、以下のような挙動になります。
- 同期的に1行単位で入力を受け取る
- キャンセルしない限り(例:
Console.CancelKeyPress
で制御)処理は進まない - 改行は返される文字列に含まれないため、そのまま画面に出力しても余分な改行が入らない
基本の使い方とサンプルコード
Console.ReadLine
の基本は、ユーザーにプロンプトを表示し、入力された文字列をそのまま使う、あるいは最低限のチェックをすることです。
プロンプトを表示して1行の文字列を取得する
// 目的: ユーザーから名前を1行で受け取り、挨拶を表示する基本例
using System;
class Program
{
static void Main()
{
Console.Write("あなたの名前を入力してください: "); // 改行なしでプロンプト表示
string? name = Console.ReadLine(); // 1行読み取り(nullの可能性あり)
if (name is null)
{
Console.WriteLine("入力が終了(EOF)しました。プログラムを終了します。");
return;
}
Console.WriteLine($"こんにちは、{name}さん!");
}
}
あなたの名前を入力してください: 山田太郎
こんにちは、山田太郎さん!
空行を許可しない基本バリデーションと再入力
入力を必須にしたい場合は、ループで空行を弾きます。
EOFは別扱いにして早期終了すると丁寧です。
// 目的: 空行を許可せず、必ず非空の文字列を取得する
using System;
class Program
{
static void Main()
{
while (true)
{
Console.Write("メールアドレスを入力してください(必須): ");
string? input = Console.ReadLine();
if (input is null)
{
Console.WriteLine("入力が終了(EOF)しました。処理を中断します。");
return; // または break; で後続処理へ
}
if (input.Length == 0)
{
Console.WriteLine("空行は無効です。もう一度入力してください。");
continue;
}
Console.WriteLine($"入力を受け付けました: {input}");
break;
}
}
}
メールアドレスを入力してください(必須):
空行は無効です。もう一度入力してください。
メールアドレスを入力してください(必須): user@example.com
入力を受け付けました: user@example.com
文字列の前処理とエラーハンドリング
ユーザー入力は余分な空白や制御文字を含むことがあります。
最初に整形し、想定外の状況(EOFやI/Oエラー)を安全に扱います。
Trimで前後の空白を除去する
Trim
は先頭・末尾の空白やタブ、改行などを取り除きます。
空白のみの入力を禁止したい場合はstring.IsNullOrWhiteSpace
が便利です。
// 目的: 前後空白の除去と空白のみの入力を拒否する
using System;
class Program
{
static void Main()
{
Console.Write("ユーザーIDを入力してください(空白のみ不可): ");
string? raw = Console.ReadLine();
if (raw is null)
{
Console.WriteLine("EOFを検知しました。終了します。");
return;
}
string trimmed = raw.Trim(); // 前後の空白・タブ・改行を除去
if (string.IsNullOrWhiteSpace(trimmed))
{
Console.WriteLine("空白のみの入力は無効です。");
return;
}
Console.WriteLine($"受理したID: [{trimmed}]");
}
}
ユーザーIDを入力してください(空白のみ不可): alice
受理したID: [alice]
null/EOFの検知と安全な処理
EOFが来たらnull
が返ります。
パイプやファイル入力では起こりやすいため、毎回の読み取りでnull
チェックを行うのが安全です。
まれにコンソールの入出力に対するIOException
が発生する可能性もあるため、必要に応じてtry-catch
でガードします。
// 目的: EOFとI/O例外を考慮した安全な読み取り
using System;
using System.IO;
class Program
{
static void Main()
{
try
{
Console.Write("コマンドを入力してください(EOFで終了): ");
string? line = Console.ReadLine();
if (line is null)
{
Console.WriteLine("EOFを検知しました。プログラムを終了します。");
return;
}
Console.WriteLine($"受信: {line}");
}
catch (IOException ex)
{
Console.Error.WriteLine($"コンソールI/Oエラー: {ex.Message}");
}
}
}
コマンドを入力してください(EOFで終了): help
受信: help
数値・日付への変換と検証
文字列入力をアプリで使いやすい型(数値や日付)に変換する場合、TryParse
系メソッドが有効です。
例外を使わずに成否を判定できます。
int.TryParseで数値入力を受け付けるサンプル
数値のみを受け付け、範囲チェックも行う例です。
// 目的: int値を入力させ、範囲(1~120)を検証する
using System;
class Program
{
static void Main()
{
while (true)
{
Console.Write("年齢を入力してください(1~120): ");
string? s = Console.ReadLine();
if (s is null)
{
Console.WriteLine("EOFを検知しました。入力を中断します。");
return;
}
if (int.TryParse(s.Trim(), out int age) && age >= 1 && age <= 120)
{
Console.WriteLine($"登録しました。年齢: {age}");
break;
}
Console.WriteLine("無効な入力です。整数で1~120の範囲で入力してください。");
}
}
}
年齢を入力してください(1~120): 0
無効な入力です。整数で1~120の範囲で入力してください。
年齢を入力してください(1~120): 25
登録しました。年齢: 25
DateTime.TryParseで日付を読み取るサンプル
ロケールの影響を受けやすい日付入力は、CultureInfo
を指定するか、フォーマットを固定するのが安全です。
// 目的: 日付の入力を "yyyy/MM/dd" 形式で受け取り、検証する
using System;
using System.Globalization;
class Program
{
static void Main()
{
var culture = CultureInfo.GetCultureInfo("ja-JP");
const string format = "yyyy/MM/dd";
while (true)
{
Console.Write($"誕生日を {format} 形式で入力してください: ");
string? s = Console.ReadLine();
if (s is null)
{
Console.WriteLine("EOFを検知しました。入力を中断します。");
return;
}
// 厳密にフォーマットを固定したい場合は TryParseExact を使用
if (DateTime.TryParseExact(s.Trim(), format, culture, DateTimeStyles.None, out DateTime date))
{
Console.WriteLine($"受理した日付: {date:yyyy年M月d日}");
break;
}
Console.WriteLine($"無効な日付です。例: 1990/12/31 のように {format} で入力してください。");
}
}
}
誕生日を yyyy/MM/dd 形式で入力してください: 1990-12-31
無効な日付です。例: 1990/12/31 のように yyyy/MM/dd で入力してください。
誕生日を yyyy/MM/dd 形式で入力してください: 1990/12/31
受理した日付: 1990年12月31日
補足として、柔軟な入力を許したい場合はDateTime.TryParse
に文化情報を渡す方法もありますが、ユーザーに一貫した入力形式を求めたい場合はTryParseExact
が適しています。
応用:複数行入力・終了条件の作り方
複数行の入力を集めたいとき、終了条件を明確に設けるとユーザーが迷いません。
典型的には「空行で終了」や「特定キーワードで終了」を使います。
空行で終了するループ入力
空行が入力されたタイミングで終了し、それまでの行をまとめます。
EOFも終了条件に含めます。
// 目的: 複数行のメモを受け取り、空行またはEOFで確定する
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var lines = new List<string>();
Console.WriteLine("メモを入力してください。空行で終了します。");
while (true)
{
string? line = Console.ReadLine();
if (line is null)
{
Console.WriteLine("EOFを検知しました。入力を終了します。");
break;
}
if (line.Length == 0) // 空行
{
break;
}
lines.Add(line);
}
Console.WriteLine("--- 入力結果 ---");
foreach (var l in lines)
{
Console.WriteLine(l);
}
}
}
メモを入力してください。空行で終了します。
一行目
二行目
三行目
--- 入力結果 ---
一行目
二行目
三行目
特定のキーワード(exit/quit)で終了する実装
ユーザーに明示的に終了コマンドを打ってもらう方式です。
大文字小文字を区別しない比較にしておくと親切です。
// 目的: exit/quit で終了する簡易コマンドループ
using System;
class Program
{
static void Main()
{
Console.WriteLine("コマンドを入力してください。exit または quit で終了します。");
while (true)
{
Console.Write("> "); // プロンプト
string? input = Console.ReadLine();
if (input is null)
{
Console.WriteLine("EOFを検知しました。終了します。");
break;
}
string command = input.Trim();
if (command.Equals("exit", StringComparison.OrdinalIgnoreCase) ||
command.Equals("quit", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("終了コマンドを受け付けました。さようなら。");
break;
}
// ここでコマンドを処理する
Console.WriteLine($"コマンド[{command}]を処理しました。");
}
}
}
コマンドを入力してください。exit または quit で終了します。
> help
コマンド[help]を処理しました。
> Quit
終了コマンドを受け付けました。さようなら。
まとめ
Console.ReadLine
はシンプルですが、EOFや空行、文化依存の解析といった現実的なケースに丁寧に対処することで、堅牢で使いやすいコンソール入力体験を実現できます。
必要に応じてTrim
とTryParse
で前処理と検証を行い、終了条件を明確にすることがポイントです。
Console.ReadKeyとの違いと使い分け
Console.ReadLine
は1行単位で文字列を取得し、Enterまでブロッキングします。フォームやコマンド入力、複数トークンの取得に向いています。
Console.ReadKey
はキー1打鍵を即座に取得します。メニューの単一キー操作(例: y/n確認)やキーイベントの検出に適します。行編集機能(バックスペースで編集する等)は自前で実装する必要があります。
使い分けの目安は「行として意味を成す入力」ならReadLine
、「単発のキー操作・ショートカット」ならReadKey
です。
エンコーディング(UTF-8)と日本語入力の注意
近年の.NET(.NET 5以降)では、WindowsでもUTF-8が既定になっている環境が増えましたが、環境によってはコンソールのコードページが異なる場合があります。
日本語が文字化けする場合は、入出力エンコーディングを明示します。
// 目的: コンソールの入力・出力エンコーディングをUTF-8に設定
using System;
using System.Text;
class Program
{
static void Main()
{
Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;
Console.Write("日本語を入力してください: ");
string? s = Console.ReadLine();
Console.WriteLine($"受信: {s}");
}
}
日本語を入力してください: こんにちは
受信: こんにちは
Windowsの古いコンソールでは、UTF-8を使うために事前にchcp 65001
(コードページの変更)やWindows Terminalの使用が必要なことがあります。
入力がファイルやパイプから供給される場合は、そのソース側のエンコーディングとも整合させてください(例: UTF-8のテキストファイルをリダイレクトする)。
これらのポイントを押さえれば、Console.ReadLine
を使ったC#のコンソール入力は、対話的で信頼性の高いものになります。