C#で最初に出会うI/Oがコンソール出力です。
中でもConsole.WriteLine
は、文字列や数値、日時、オブジェクトなどを見やすく表示するための基本メソッドです。
本記事では、基本から書式指定、ローカライズ、エラー出力、実用サンプル、注意点・ベストプラクティスまで丁寧に解説します。
Console.WriteLineとは?
役割と基本概念
Console.WriteLine
は標準出力(Standard Output)へ内容を表示し、行末に改行を追加するメソッドです。
改行は環境依存のEnvironment.NewLine
(WindowsはCRLF、Unix系はLF)で表されます。
引数には文字列だけでなく、数値、ブール値、日時、任意のオブジェクトを渡せます。
オーバーロードの概略
Console.WriteLine
には多くのオーバーロードがあります。
代表的なものは以下です。
WriteLine()
(空行)WriteLine(string value)
WriteLine(object value)
(ToString()
で文字列化)WriteLine(string format, params object?[] args)
(複合書式)WriteLine(ReadOnlySpan<char> value)
などの効率的なオーバーロード
バッファとフラッシュ
コンソールはバッファリングされることがあり、改行付き出力は区切りとして扱われるため、行単位で出力が見やすくなります。
必要に応じてConsole.Out.Flush()
で明示的にフラッシュできます。
Console.WriteLineの基本的な使い方
文字列を表示する
最も基本的な使い方は文字列をそのまま表示することです。
サンプルコード
using System;
class Program
{
static void Main()
{
// 単純な文字列の表示
Console.WriteLine("Hello, Console!");
// 空行の出力
Console.WriteLine();
// 日本語や記号の表示(文字化けする場合は後述のOutputEncodingを参照)
Console.WriteLine("こんにちは、世界!★");
}
}
実行結果例
Hello, Console!
こんにちは、世界!★
変数・数値・ブール値を表示する
変数はそのまま渡せます。
オブジェクトはToString()
が呼ばれます。
サンプルコード
using System;
class Program
{
static void Main()
{
int count = 3;
double price = 1234.5;
bool isActive = true;
Console.WriteLine(count); // 数値
Console.WriteLine(price); // 小数
Console.WriteLine(isActive); // ブール値
object any = new Version(1, 2, 3, 4);
Console.WriteLine(any); // object → ToString()が呼ばれる
}
}
実行結果例
3
1234.5
True
1.2.3.4
文字列連結と改行の扱い
文字列は+
演算子で連結できますが、読みやすさやパフォーマンスの観点からは文字列補間や書式指定を推奨します。
改行には\n
やEnvironment.NewLine
を使えます。
サンプルコード
using System;
class Program
{
static void Main()
{
string first = "Alice";
string last = "Smith";
// 連結
Console.WriteLine("Name: " + first + " " + last);
// 改行(環境依存で安全な方法)
Console.WriteLine("Line1" + Environment.NewLine + "Line2");
// エスケープ(\n, \t, \\ など)
Console.WriteLine("Path: C:\\tools\\app.exe\t<- ここにあります");
}
}
実行結果例
Name: Alice Smith
Line1
Line2
Path: C:\tools\app.exe <- ここにあります
Console.WriteとConsole.WriteLineの違い
Console.Write
は改行なし、Console.WriteLine
は改行ありです。
連続出力の区切りに注意します。
サンプルコード
using System;
class Program
{
static void Main()
{
Console.Write("A");
Console.Write("B");
Console.Write("C");
Console.WriteLine(); // ここで改行
Console.WriteLine("D"); // 最後に改行込み
}
}
実行結果例
ABC
D
文字列補間と書式指定で見やすく表示する
文字列補間$””で変数値を埋め込む
$"..."
を用いると、変数や式を{}
内に直接書け、可読性が高まります。
サンプルコード
using System;
class Program
{
static void Main()
{
string user = "Alice";
int age = 28;
Console.WriteLine($"User: {user}, Age: {age}");
// 式もOK
Console.WriteLine($"Next year: {age + 1}");
}
}
実行結果例
User: Alice, Age: 28
Next year: 29
複合書式{0}とstring.Formatの使い方
古典的な複合書式は、テンプレートと引数の位置指定で整列や桁数指定が行えます。
サンプルコード
using System;
class Program
{
static void Main()
{
string item = "Notebook";
int qty = 12;
double price = 1234.5;
Console.WriteLine("Item: {0}, Qty: {1}, Price: {2}", item, qty, price);
// 整列(左詰め-10、右詰め10)
Console.WriteLine("|{0,-10}|{1,10}|", item, qty);
// string.Formatで一旦文字列化してから出力
string line = string.Format("合計: {0}", qty * price);
Console.WriteLine(line);
}
}
実行結果例
Item: Notebook, Qty: 12, Price: 1234.5
|Notebook | 12|
合計: 14814
数値フォーマット(小数・通貨・パーセント)
数値は書式指定文字列で見やすく整形できます。
既定カルチャの影響を受けます(後述のCultureInfoで制御可能)。
サンプルコード
using System;
using System.Globalization;
class Program
{
static void Main()
{
double value = 12345.6789;
Console.WriteLine($"F2(小数2桁): {value:F2}");
Console.WriteLine($"N0(桁区切り): {value:N0}");
Console.WriteLine($"C(通貨): {value:C}");
Console.WriteLine($"P(パーセント): {0.256:P1}"); // 25.6%
Console.WriteLine($"カスタム('#,0.00'): {value:#,0.00}");
// CultureInfoを指定して通貨の記号や桁区切りを切り替え
var ja = new CultureInfo("ja-JP");
var us = new CultureInfo("en-US");
Console.WriteLine($"ja-JP通貨: {value.ToString("C", ja)}");
Console.WriteLine($"en-US通貨: {value.ToString("C", us)}");
}
}
実行結果例
F2(小数2桁): 12345.68
N0(桁区切り): 12,346
C(通貨): ¥12,345.68
P(パーセント): 25.6 %
カスタム('#,0.00'): 12,345.68
ja-JP通貨: ¥12,345.68
en-US通貨: $12,345.68
日付・時刻のフォーマット(DateTime/TimeSpan)
日時や時間間隔も書式指定で自在に表現できます。
サンプルコード
using System;
using System.Globalization;
class Program
{
static void Main()
{
var dt = new DateTime(2025, 5, 1, 14, 30, 5, DateTimeKind.Local);
var ts = new TimeSpan(1, 2, 3, 4, 567); // 1日2時間3分4秒567ms
// 標準書式
Console.WriteLine($"短い日時(g): {dt:g}");
Console.WriteLine($"長い日時(F): {dt:F}");
Console.WriteLine($"ISOっぽい(o): {dt:o}");
Console.WriteLine($"RFC1123(R): {dt:R}");
// カスタム書式
Console.WriteLine(dt.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine(ts.ToString(@"d\.hh\:mm\:ss\.fff"));
// CultureInfoで曜日名・月名などをローカライズ
var us = new CultureInfo("en-US");
Console.WriteLine(dt.ToString("dddd, MMMM dd, yyyy", us));
}
}
実行結果例
短い日時(g): 2025/05/01 14:30
長い日時(F): 2025年5月1日 14:30:05
ISOっぽい(o): 2025-05-01T14:30:05.0000000+09:00
RFC1123(R): Thu, 01 May 2025 05:30:05 GMT
2025-05-01 14:30:05
1.02:03:04.567
Thursday, May 01, 2025
出力を整える実用テクニック
複数行出力・タブ・エスケープシーケンス
行の区切りやインデントには改行やタブを使います。
複数行のリテラルには逐語的文字列(@)も便利です。
サンプルコード
using System;
class Program
{
static void Main()
{
Console.WriteLine("Header\tColumn1\tColumn2");
Console.WriteLine("Data\t100\t200");
// 複数行(逐語的文字列: バックスラッシュや改行をそのまま)
Console.WriteLine(@"C:\logs\app.log
C:\logs\error.log");
// 環境依存しない改行
Console.WriteLine("LineA" + Environment.NewLine + "LineB");
}
}
実行結果例
Header Column1 Column2
Data 100 200
C:\logs\app.log
C:\logs\error.log
LineA
LineB
CultureInfoでローカライズ表示
数値や日時の既定フォーマットは「現在カルチャ」に依存します。
CultureInfo.CurrentCulture
や明示指定で制御します。
サンプルコード
using System;
using System.Globalization;
class Program
{
static void Main()
{
double n = 12345.67;
DateTime d = new(2025, 8, 1);
var fr = new CultureInfo("fr-FR");
var ja = new CultureInfo("ja-JP");
Console.WriteLine(n.ToString("N2", fr)); // 12 345,67
Console.WriteLine(d.ToString("D", ja)); // 2025年8月1日
}
}
Console.OutputEncodingで文字化け対策
Windowsの既定コードページでは日本語や絵文字が文字化けすることがあります。
UTF-8を明示しましょう。
サンプルコード
using System;
using System.Text;
class Program
{
static void Main()
{
// UTF-8に設定(BOMなし)
Console.OutputEncoding = new UTF8Encoding(false);
Console.WriteLine("日本語と絵文字😊の表示テスト");
}
}
日本語と絵文字😊の表示テスト
注意
- 端末側がUTF-8に対応している必要があります。Windowsの旧コンソールでは
chcp 65001
や「ベータ: Unicode UTF-8を使用」に設定が必要な場合があります。 - 出力をファイルへリダイレクトする場合も、意図したエンコーディングで保存されます。
Console.Error.WriteLineでエラー出力を分ける
標準エラー(Standard Error)に出力すると、通常ログとエラーを別ストリームで扱えます。
リダイレクト時にも分離可能です。
サンプルコード
using System;
class Program
{
static void Main()
{
Console.WriteLine("Info: アプリ開始");
Console.Error.WriteLine("Error: 予期せぬエラーが発生しました");
}
}
実行方法の例
- 標準出力をファイル、標準エラーを画面に残す(PowerShell):
dotnet run > out.txt
- 標準エラーも別ファイルへ(PowerShell):
dotnet run 1> out.txt 2> err.txt
実用サンプルコード集
ループでのログ出力とインデント
処理の階層に応じてインデントを付けると読みやすくなります。
サンプルコード
using System;
class Program
{
static void Log(string message, int indent = 0)
{
// インデント幅は2スペース×レベル
string pad = new string(' ', indent * 2);
Console.WriteLine($"{pad}{message}");
}
static void Main()
{
Log("Start");
for (int i = 0; i < 3; i++)
{
Log($"Loop i={i}", 1);
for (int j = 0; j < 2; j++)
{
Log($"Inner j={j}", 2);
}
}
Log("Done");
}
}
実行結果例
Start
Loop i=0
Inner j=0
Inner j=1
Loop i=1
Inner j=0
Inner j=1
Loop i=2
Inner j=0
Inner j=1
Done
例外メッセージとスタックトレースを表示
例外情報はException.ToString()
でメッセージとスタックトレースをまとめて取得できます。
サンプルコード
using System;
class Program
{
static void Main()
{
try
{
ThrowNested();
}
catch (Exception ex)
{
Console.Error.WriteLine("=== 例外発生 ===");
// 例外メッセージ
Console.Error.WriteLine(ex.Message);
// スタックトレース
Console.Error.WriteLine(ex.StackTrace);
// まとめて(型, メッセージ, スタック, InnerExceptionまで含む)
Console.Error.WriteLine("--- ex.ToString() ---");
Console.Error.WriteLine(ex.ToString());
}
}
static void ThrowNested()
{
try
{
int x = int.Parse("not-an-int");
}
catch (FormatException fe)
{
throw new InvalidOperationException("パースに失敗しました。", fe);
}
}
}
実行結果例(抜粋)
=== 例外発生 ===
パースに失敗しました。
(スタックトレースが表示)
--- ex.ToString() ---
System.InvalidOperationException: パースに失敗しました。 ---> System.FormatException: Input string was not in a correct format.
at ...
オブジェクトの表示(ToStringとJSONシリアライズ)
ToString()
のオーバーライドで簡易表示、詳細はJSONで整形すると便利です。
サンプルコード
using System;
using System.Text.Json;
class User
{
public string Name { get; init; } = "";
public int Age { get; init; }
// ログ向けの簡易表現
public override string ToString() => $"{Name}({Age})";
}
class Program
{
static void Main()
{
var user = new User { Name = "Alice", Age = 30 };
Console.WriteLine(user); // ToString()
// JSONで詳細表示(インデント有り)
var json = JsonSerializer.Serialize(user, new JsonSerializerOptions
{
WriteIndented = true
});
Console.WriteLine(json);
}
}
実行結果例
Alice(30)
{
"Name": "Alice",
"Age": 30
}
書式付きテーブル風の出力
列幅と整列指定でテーブル風に整えます。
サンプルコード
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
var rows = new List<(string Name, int Qty, double Price)>
{
("Notebook", 3, 1200.0),
("Pencil", 12, 80.5),
("Eraser", 2, 50),
};
Console.WriteLine("| {0,-10} | {1,5} | {2,10} | {3,12} |", "Item", "Qty", "Price", "Subtotal");
Console.WriteLine(new string('-', 52));
foreach (var r in rows)
{
double subtotal = r.Qty * r.Price;
Console.WriteLine("| {0,-10} | {1,5} | {2,10:F2} | {3,12:N2} |",
r.Name, r.Qty, r.Price, subtotal);
}
}
}
実行結果例
| Item | Qty | Price | Subtotal |
----------------------------------------------------
| Notebook | 3 | 1200.00 | 3,600.00 |
| Pencil | 12 | 80.50 | 966.00 |
| Eraser | 2 | 50.00 | 100.00 |
よくあるエラー・注意点とベストプラクティス
null/Nullableの表示と安全な扱い
文字列連結や補間にnull
を含むと、補間では空文字列、連結では"文字列" + null
がそのまま"文字列"
になりますが、null.ToString()
は例外になります。
?.
や??
演算子で安全に表示しましょう。
サンプルコード
using System;
class Program
{
static void Main()
{
string? s = null;
// OK: nullは空として扱われる
Console.WriteLine($"value='{s}'"); // value=''
// 安全なフォールバック
Console.WriteLine($"value='{s ?? "(null)"}'");
// NG: 直接ToString()は例外
// Console.WriteLine(s.ToString()); // NullReferenceException
// オブジェクトでも同様
object? o = null;
Console.WriteLine($"{o?.ToString() ?? "(null object)"}");
}
}
パフォーマンス(連結 vs 補間 vs StringBuilder)
コンソール出力自体が比較的高コストなI/Oのため、単発の出力では連結方法の差は体感しにくいです。
加工が複雑・繰り返し文字列構築が多い場合はStringBuilder
が有利です。
ただし、文字列補間($)は可読性が高く、内部的に効率化されることも多いため、まずは補間を使い、ボトルネック時にStringBuilder
を検討します。
例:大量結合はStringBuilder
using System;
using System.Text;
class Program
{
static void Main()
{
var sb = new StringBuilder();
for (int i = 0; i < 5_000; i++)
{
sb.Append("Line ").Append(i).Append('\n');
}
Console.WriteLine(sb.ToString());
}
}
出力のリダイレクトとバッファリングの注意
>
や2>
でリダイレクトすると、出力はファイルや他プロセスへ流れます。このときConsole.IsOutputRedirected
やConsole.IsErrorRedirected
がtrue
になります。
リダイレクト先によってはバッファリングされ、出力がすぐ見えない場合があります。必要に応じてConsole.Out.Flush()
やAutoFlush
を使います。
明示的にファイルへ切り替える場合はConsole.SetOut
を利用します。
サンプルコード(UTF-8でファイルへ)
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
using var writer = new StreamWriter("log.txt", append: false, new UTF8Encoding(false))
{
AutoFlush = true
};
Console.SetOut(writer);
Console.WriteLine("ファイルに書き込まれます");
Console.Out.Flush(); // 念のため
}
}
非同期コード・並列実行時のConsole出力の衝突対策
複数スレッドやasync/await
で同時に出力すると、行が混ざることがあります。
1回のWriteLine
で1行を完成させ、分割書き込みを避ける。
共有ロックで同期する、あるいは専用スレッドにログをキューイングする。
Console.Out
はスレッドセーフですが、複数行に跨る書き込みは混在し得ます。
サンプルコード(ロックで保護)
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static readonly object _lock = new();
static async Task Main()
{
var t1 = Task.Run(() => LogMany("A"));
var t2 = Task.Run(() => LogMany("B"));
await Task.WhenAll(t1, t2);
}
static void LogMany(string tag)
{
for (int i = 0; i < 5; i++)
{
string line = $"{tag}: message {i}";
lock (_lock) // 1行単位で同期
{
Console.WriteLine(line);
}
Thread.Sleep(10);
}
}
}
サンプルコード(非同期書き込み)
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Console.Out.WriteLineAsyncはTextWriterの非同期API
await Console.Out.WriteLineAsync("Async line 1");
await Console.Out.WriteLineAsync("Async line 2");
}
}
まとめ
Console.WriteLine
はC#における基本的かつ強力な出力手段です。
単純な文字列表示から、補間・書式指定による整形、数値や日時のローカライズ、エンコーディングの指定、エラー出力の分離まで、利用範囲は広く実務でも頻出します。
まずは読みやすさを優先して文字列補間を活用し、必要に応じて複合書式やCultureInfo
、Console.OutputEncoding
を使い分けましょう。
並列・非同期での出力は1行単位の同期やキューイングで混在を避け、リダイレクトやファイル出力ではエンコーディングとバッファリングに注意することが大切です。
これらのコツを押さえれば、開発中のデバッグや運用ログが一段と読みやすく、役立つものになります。