在撰寫 C# 主控台應用程式時,常見需求是讓使用者從鍵盤輸入多行文字,並以 Ctrl+Z(Windows 系統下的 EOF,End-Of-File 標記)作為輸入結束的信號。然而,ConsoleKey 列舉中並沒有直接對應 Ctrl 鍵的值,導致許多開發者不知道如何正確偵測 Ctrl+Z。本文將分享透過 單步偵錯 觀察 ConsoleKeyInfo.KeyChar 數值,並利用 ASCII 控制字元 0x1A (十進位 26) 來精準判斷 Ctrl+Z 的完整解決方案,並附上模擬 DOS COPY CON 指令的完整範例程式碼。
Console.ReadKey() 回傳 ConsoleKeyInfo 結構,包含 Key(ConsoleKey 列舉)、KeyChar(實際字元)、Modifiers(Shift/Alt/Ctrl 修飾鍵)。ConsoleKey 列舉沒有 Ctrl 鍵,但 Ctrl+Z 產生的 KeyChar 為 ASCII SUB (Substitute, 0x1A / 十進位 26)。(int)cki.KeyChar == 26 或 cki.KeyChar == '\u001A'。以下程式碼實作一個 DOSCopyCon 類別,支援:
copy con filename.txt:從主控台讀取輸入,直到 Ctrl+Z 或 End 鍵,寫入檔案。copy filename.txt con:讀取檔案內容輸出到主控台。StreamWriter/StreamReader)。using System;
using System.IO;
namespace ConsoleCopyConDemo
{
///
/// 模擬 DOS COPY CON 指令行為
///
public class DOSCopyCon
{
private string _inputCommandText = "";
public DOSCopyCon(string inputCommandText = "")
{
_inputCommandText = inputCommandText;
}
public string InputCommandText
{
get => _inputCommandText;
set
{
if (!string.IsNullOrWhiteSpace(value))
_inputCommandText = value;
}
}
///
/// 執行 COPY CON 指令
///
public void ExecuteCopyConCommand(string inputCommandText = null)
{
if (!string.IsNullOrWhiteSpace(inputCommandText))
_inputCommandText = inputCommandText;
if (string.IsNullOrWhiteSpace(_inputCommandText))
{
Console.WriteLine("** 指令不可為空 **");
return;
}
DoCopyCon();
}
private void DoCopyCon()
{
// 解析指令:copy con file.txt 或 copy file.txt con
var parts = _inputCommandText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 3 || !parts[0].Equals("copy", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("指令格式錯誤!用法:copy con <檔名> 或 copy <檔名> con");
return;
}
// 情況 1:copy con <檔名> → 從主控台輸入寫入檔案
if (parts[1].Equals("con", StringComparison.OrdinalIgnoreCase))
{
string targetFile = parts[2];
Console.WriteLine($"開始寫入檔案 {targetFile} (按 Ctrl+Z 或 End 結束):");
string inputText = "";
ConsoleKeyInfo cki;
while (true)
{
cki = Console.ReadKey(true); // true = 不顯示輸入字元
// 偵測 Ctrl+Z (ASCII 26) 或 End 鍵
if (cki.Key == ConsoleKey.End || (int)cki.KeyChar == 26)
break;
// 處理 Backspace
if (cki.Key == ConsoleKey.Backspace && inputText.Length > 0)
{
inputText = inputText.Substring(0, inputText.Length - 1);
Console.Write("\b \b"); // 視覺上刪除一字元
continue;
}
// 忽略其他控制字元(如 Ctrl+C 等)
if (!char.IsControl(cki.KeyChar))
inputText += cki.KeyChar;
}
try
{
File.WriteAllText(targetFile, inputText);
Console.WriteLine($"\n寫入檔案 {targetFile} 成功。");
}
catch (Exception ex)
{
Console.WriteLine($"\n寫入失敗:{ex.Message}");
}
return;
}
// 情況 2:copy <檔名> con → 讀取檔案輸出到主控台
if (parts[2].Equals("con", StringComparison.OrdinalIgnoreCase))
{
string sourceFile = parts[1];
Console.WriteLine($"開始讀取檔案 {sourceFile}:");
try
{
string content = File.ReadAllText(sourceFile);
Console.WriteLine(content);
Console.WriteLine("讀取完成。");
}
catch (FileNotFoundException)
{
Console.WriteLine($"找不到檔案:{sourceFile}");
}
catch (Exception ex)
{
Console.WriteLine($"讀取失敗:{ex.Message}");
}
return;
}
Console.WriteLine("不支援的指令格式。");
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== DOS COPY CON 模擬器 ===");
Console.WriteLine("指令範例:");
Console.WriteLine(" copy con test.txt (輸入文字,Ctrl+Z 結束)");
Console.WriteLine(" copy test.txt con (顯示檔案內容)");
Console.WriteLine();
var app = new DOSCopyCon();
while (true)
{
Console.Write("\n> ");
string cmd = Console.ReadLine();
if (string.IsNullOrWhiteSpace(cmd)) continue;
if (cmd.Equals("exit", StringComparison.OrdinalIgnoreCase)) break;
app.ExecuteCopyConCommand(cmd);
}
}
}
}
cki = Console.ReadKey(true); // true = 不在螢幕顯示字元
if (cki.Key == ConsoleKey.End || (int)cki.KeyChar == 26)
break;
Console.ReadKey(true) 讓按鍵不自動回顯,適合自行處理 Backspace 等編輯鍵。(int)cki.KeyChar == 26 精準捕捉 Ctrl+Z(ASCII SUB 字元)。if (cki.Key == ConsoleKey.Backspace && inputText.Length > 0)
{
inputText = inputText.Substring(0, inputText.Length - 1);
Console.Write("\b \b"); // 移動游標左、覆寫空白、再移回
continue;
}
讓使用者在輸入過程中能刪除錯誤字元,體驗更接近真實編輯器。
.NET Framework 2.0+ 內建靜態方法,自動處理 StreamWriter/StreamReader 的建立與關閉,程式碼更簡潔安全。
| 誤區 | 正確做法 |
|---|---|
嘗試用 cki.Key == ConsoleKey.Z && cki.Modifiers.HasFlag(ConsoleModifiers.Control) | ConsoleKey 無 Ctrl,Modifiers 只能偵測 Shift/Alt,無法偵測 Ctrl 組合鍵 |
使用 Console.ReadLine() 讀取多行 | ReadLine 遇到 Enter 就回傳,無法處理 Ctrl+Z 作為 EOF |
直接比較 cki.KeyChar == (char)26 | 雖然可行,但建議寫成 (int)cki.KeyChar == 26 或 '\u001A' 以提升可讀性 |
在 C# 主控台應用程式中處理 Ctrl+Z,關鍵在於理解 ConsoleKeyInfo.KeyChar 會回傳 ASCII 控制字元 26 (0x1A)。透過單步偵錯觀察實際數值,配合 Console.ReadKey(true) 自行處理回顯與編輯鍵,即可實作出健全的多行輸入功能。本文提供的 DOSCopyCon 完整範例已包含指令解析、輸入編輯、檔案讀寫與錯誤處理,可直接套用於實務專案中。