C# Parser入門:文字列・JSON・CSVを解析する方法とおすすめライブラリを実例で解説
はじめに
C#で開発していると、「文字列を数値に変換したい」「JSONをクラスに変換したい」「CSVファイルを読み込みたい」「ログ文字列から必要な情報だけ抜き出したい」といった場面が頻繁にあります。こうした入力データをプログラムで扱いやすい形に変換する処理が、広い意味でのParserです。
「c# parser」と検索する人の多くは、単に構文解析器を作りたいだけでなく、int.ParseやDateTime.Parseの使い方、JSON Parser、CSV Parser、正規表現による文字列解析、独自フォーマットの読み取り方法まで知りたいはずです。
この記事では、C# Parserの基本から、文字列・JSON・CSV・独自フォーマットの解析方法、おすすめライブラリ、実装時の注意点まで、実例を交えて解説します。
1. C# Parserとは?解析処理の基本を初心者向けに解説
1-1. Parserの意味とC#で使われる場面
Parserとは、入力された文字列やファイルの内容を読み取り、プログラムで扱いやすいデータ構造に変換する仕組みです。
たとえば、次のような処理はすべてParserに関係します。
C#int age = int.Parse("30");
DateTime date = DateTime.Parse("2026-06-07");
この例では、文字列 "30" を整数の 30 に、文字列 "2026-06-07" を DateTime に変換しています。単純な型変換に見えますが、内部的には「入力文字列を解析して、意味のある値に変換する」処理です。
C#でParserが使われる場面は、Web APIのレスポンス解析、CSVファイルの取り込み、ログ分析、設定ファイルの読み込み、ユーザー入力の検証、独自DSLの構文解析など多岐にわたります。
1-2. ParseとParserの違い
Parseは「解析する」という動詞で、C#ではメソッド名としてよく使われます。代表例は int.Parse、double.Parse、DateTime.Parse です。
一方、Parserは「解析を行う仕組み」や「解析器」を指します。たとえば、JSON Parser、CSV Parser、構文Parserのように使われます。
つまり、簡単に整理すると次のようになります。
| 用語 | 意味 |
|---|---|
| Parse | 解析する処理・メソッド |
| Parser | 解析を行う仕組み・クラス・ライブラリ |
| Parsing | 解析処理そのもの |
C#でParserを理解するには、まず標準の Parse 系メソッドから入り、次にJSONやCSVのような構造化データの解析へ進むと理解しやすくなります。
1-3. 文字列解析・構文解析・データ変換の違い
Parserと一口に言っても、対象によって意味が少し変わります。
文字列解析は、入力文字列から必要な値を取り出す処理です。たとえば "ID:1001,Name:Taro" からIDと名前を取り出すような処理です。
構文解析は、プログラミング言語や独自フォーマットの文法に従って、入力をトークンや構文木に変換する処理です。SQL、数式、検索条件、独自DSLなどを扱う場合に必要になります。
データ変換は、解析した結果をC#のオブジェクト、数値、日付、リストなどに変換する処理です。JSONをクラスに変換する Deserialize も、広い意味ではParser処理です。
1-4. C#でParserが必要になる代表的なケース
C#でParserが必要になる代表的なケースは次のとおりです。
| ケース | 例 |
|---|---|
| ユーザー入力の変換 | 文字列を数値や日付に変換する |
| Web API連携 | JSONレスポンスをクラスに変換する |
| ファイル取り込み | CSVやTSVを読み込む |
| ログ解析 | 日時、ログレベル、メッセージを抽出する |
| 設定ファイル解析 | key=value形式を読み込む |
| 独自言語・DSL | 数式、検索条件、ルール定義を解析する |
単純なデータなら標準機能で十分ですが、引用符、エスケープ、入れ子構造、複雑な文法が出てくる場合は専用ライブラリを使うのが安全です。
1-5. この記事で扱う解析対象:文字列・JSON・CSV・独自フォーマット
この記事では、C# Parserの実践的な対象として、次の4つを扱います。
まず、数値や日付などの文字列Parserです。これはC#の基本であり、Parse、TryParse、Split、Regexを理解することが重要です。
次にJSON Parserです。C#では標準の System.Text.Json や定番ライブラリの Newtonsoft.Json がよく使われます。
さらにCSV Parserです。単純なCSVなら Split でも処理できますが、実務では CsvHelper のような専用ライブラリが役立ちます。
最後に、ログや独自設定ファイルなどの独自フォーマットParserです。正規表現で十分なケースもありますが、複雑な構文ではParser CombinatorやANTLRのような仕組みが有効です。
2. C#で文字列をParserする基本方法
2-1. int.Parse/double.Parse/DateTime.Parseの基本
C#で最も基本的なParserは、標準型に用意されている Parse メソッドです。
C#int count = int.Parse("123");
double price = double.Parse("123.45");
DateTime date = DateTime.Parse("2026-06-07");
Console.WriteLine(count);
Console.WriteLine(price);
Console.WriteLine(date);
Parseは、入力文字列が正しい形式であることを前提に変換します。そのため、入力値が必ず正しいと保証できる場合にはシンプルに書けます。
ただし、次のような文字列を渡すと例外が発生します。
C#int value = int.Parse("abc"); // FormatException
ユーザー入力や外部ファイルの内容を扱う場合、入力値が常に正しいとは限りません。そのため、実務では Parse よりも TryParse を使う場面が多くなります。
2-2. TryParseを使って例外を避ける安全な解析方法
TryParseは、解析に成功したかどうかを bool で返すメソッドです。失敗しても例外を投げないため、安全なParser処理に向いています。
C#string input = "123";
if (int.TryParse(input, out int value))
{
Console.WriteLine($"変換成功: {value}");
}
else
{
Console.WriteLine("数値として解析できませんでした。");
}
日付でも同じように使えます。
C#string input = "2026-06-07";
if (DateTime.TryParse(input, out DateTime date))
{
Console.WriteLine($"日付: {date:yyyy/MM/dd}");
}
else
{
Console.WriteLine("日付として解析できませんでした。");
}
ユーザー入力、CSV、ログ、外部APIレスポンスなど、失敗する可能性がある入力には TryParse を使うのが基本です。
2-3. ParseとTryParseの使い分け
ParseとTryParseは、次のように使い分けるとよいでしょう。
| メソッド | 向いている場面 |
|---|---|
| Parse | 入力値が必ず正しいと分かっている場合 |
| TryParse | 入力値が不正な可能性がある場合 |
| Parse + try-catch | 例外内容を詳細に扱いたい場合 |
たとえば、プログラム内部で生成した文字列を変換するだけなら Parse でも問題ありません。
C#string internalValue = "100";
int value = int.Parse(internalValue);
一方、フォーム入力やファイル入力では TryParse を使うべきです。
C#string userInput = "abc";
if (!int.TryParse(userInput, out int result))
{
Console.WriteLine("入力値は整数ではありません。");
}
Parser設計では、「失敗する可能性がある入力」を前提にすることが重要です。
2-4. Splitを使った簡単な文字列解析
区切り文字で分かれた単純な文字列なら、Splitで解析できます。
C#string line = "1001,Taro,25";
string[] parts = line.Split(',');
int id = int.Parse(parts[0]);
string name = parts[1];
int age = int.Parse(parts[2]);
Console.WriteLine($"{id}: {name} ({age})");
Splitは簡単で分かりやすいため、固定フォーマットの簡易Parserに向いています。
ただし、次のような入力には注意が必要です。
1001,"Taro,Yamada",25
名前にカンマが含まれている場合、単純に Split(',') すると意図しない位置で分割されます。CSVのように引用符やエスケープが絡む形式では、専用Parserを使う方が安全です。
2-5. 正規表現Regexを使った柔軟な文字列Parser
複雑な文字列から特定のパターンを抽出したい場合は、Regexが便利です。
たとえば、ログ文字列から日時、ログレベル、メッセージを抽出する例です。
C#using System.Text.RegularExpressions;
string log = "2026-06-07 10:30:15 [INFO] Application started";
var match = Regex.Match(
log,
@"^(?<date>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>\w+)\] (?<message>.+)$"
);
if (match.Success)
{
string date = match.Groups["date"].Value;
string level = match.Groups["level"].Value;
string message = match.Groups["message"].Value;
Console.WriteLine(date);
Console.WriteLine(level);
Console.WriteLine(message);
}
正規表現は、ログ解析、ID抽出、メールアドレス検証、固定パターンのテキスト解析に向いています。
ただし、入れ子構造や複雑な文法を解析するには不向きです。たとえば、括弧が何重にも入れ子になる数式Parserや、独自言語の構文解析には、Parser CombinatorやANTLRのような仕組みを検討した方がよいです。
2-6. 文字列Parserでよくあるエラーと対処法
文字列Parserでよくあるエラーは、次のようなものです。
| エラー | 原因 | 対処法 |
|---|---|---|
| FormatException | 形式が不正 | TryParseを使う |
| OverflowException | 数値範囲を超えている | 型の範囲を確認する |
| IndexOutOfRangeException | Split結果の要素数不足 | 要素数をチェックする |
| NullReferenceException | nullを解析しようとした | string.IsNullOrWhiteSpaceで確認する |
| カルチャ差異 | 小数点や日付形式が環境依存 | CultureInfoを指定する |
たとえば、空文字やnullを処理する場合は、先に入力チェックを行います。
C#string? input = "";
if (string.IsNullOrWhiteSpace(input))
{
Console.WriteLine("入力が空です。");
}
else if (int.TryParse(input, out int value))
{
Console.WriteLine(value);
}
安全なC# Parserを作るには、「解析する前に入力を検証する」ことが重要です。
3. C#でJSONをParserする方法
3-1. System.Text.JsonでJSONを解析する基本
C#でJSONをParserする場合、まず候補になるのが標準の System.Text.Json です。JsonSerializerは、JSONとC#オブジェクトのシリアライズ・デシリアライズを行うための機能を提供しています。
基本的なJSON文字列を解析する例を見てみましょう。
C#using System.Text.Json;
string json = """
{
"id": 1,
"name": "Taro",
"age": 30
}
""";
User? user = JsonSerializer.Deserialize<User>(json);
Console.WriteLine(user?.Name);
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
Deserialize<T>を使うと、JSON文字列を指定したC#クラスに変換できます。APIレスポンスを扱う場合に非常によく使われる方法です。
3-2. JsonSerializer.Deserializeを使ったオブジェクト変換
JsonSerializer.Deserializeは、JSON Parserとして最も基本的な使い方です。
C#using System.Text.Json;
string json = """
{
"productId": 101,
"productName": "Keyboard",
"price": 5980
}
""";
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
Product? product = JsonSerializer.Deserialize<Product>(json, options);
Console.WriteLine(product?.ProductName);
public class Product
{
public int ProductId { get; set; }
public string? ProductName { get; set; }
public decimal Price { get; set; }
}
PropertyNameCaseInsensitive = true を指定すると、JSON側のプロパティ名とC#側のプロパティ名の大文字小文字の違いを吸収できます。
JSON Parserでは、クラス設計も重要です。JSONに存在しないプロパティは既定値になり、型が合わない場合は例外になることがあります。
3-3. JsonDocumentを使って必要な値だけ取得する方法
JSON全体をクラスに変換する必要がない場合は、JsonDocumentを使う方法もあります。特定の値だけ取得したい場合に便利です。
C#using System.Text.Json;
string json = """
{
"status": "ok",
"data": {
"count": 10,
"message": "success"
}
}
""";
using JsonDocument document = JsonDocument.Parse(json);
JsonElement root = document.RootElement;
string status = root.GetProperty("status").GetString()!;
int count = root.GetProperty("data").GetProperty("count").GetInt32();
Console.WriteLine(status);
Console.WriteLine(count);
JsonDocumentは、JSONの構造を動的にたどりたい場合に向いています。ただし、プロパティが存在しない場合に例外が発生するため、実務では TryGetProperty を使うと安全です。
C#if (root.TryGetProperty("status", out JsonElement statusElement))
{
Console.WriteLine(statusElement.GetString());
}
3-4. Newtonsoft.Jsonを使ったJSON Parserの実装
Newtonsoft.Json、通称Json.NETは、長く使われてきた定番のJSONライブラリです。公式サイトでも、オープンソースで商用利用可能なJSONライブラリとして紹介されています。
インストール後、次のように使えます。
C#using Newtonsoft.Json;
string json = """
{
"id": 1,
"name": "Hanako"
}
""";
User? user = JsonConvert.DeserializeObject<User>(json);
Console.WriteLine(user?.Name);
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
}
Newtonsoft.Jsonは、柔軟な設定、古い.NET環境への対応、動的JSON操作、既存プロジェクトとの互換性が必要な場合に便利です。
3-5. System.Text.JsonとNewtonsoft.Jsonの違い
System.Text.JsonとNewtonsoft.Jsonは、どちらもC#でJSONをParserするための代表的な選択肢です。
| 項目 | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| 位置づけ | .NET標準のJSON機能 | 定番の外部ライブラリ |
| パフォーマンス | 高速・軽量を重視 | 柔軟性が高い |
| 設定の自由度 | 標準用途には十分 | 高度なカスタマイズに強い |
| 既存資産 | 新規開発向き | 既存プロジェクトで多い |
| 動的操作 | JsonDocument / JsonNode | JObject / JTokenが便利 |
新規の.NETアプリケーションでは、まず System.Text.Json を検討するとよいでしょう。一方、既存コードが Newtonsoft.Json に依存している場合や、複雑な変換ルールが必要な場合は Newtonsoft.Json が適しています。
3-6. JSON解析で発生しやすい例外とデバッグ方法
JSON Parserでよく発生する問題は、JSON形式の不正、型の不一致、プロパティ名の不一致、null値の扱いです。
C#try
{
User? user = JsonSerializer.Deserialize<User>(json);
}
catch (JsonException ex)
{
Console.WriteLine($"JSON解析エラー: {ex.Message}");
}
デバッグ時は、次の点を確認しましょう。
| 確認項目 | 内容 |
|---|---|
| JSONが正しい形式か | カンマ抜け、波括弧の閉じ忘れ |
| 型が一致しているか | 数値に文字列が入っていないか |
| プロパティ名が合っているか | camelCaseとPascalCaseの違い |
| nullが許容されるか | nullable設定と整合しているか |
| 配列と単一オブジェクトを混同していないか | List<T>が必要な場合がある |
たとえばJSONが配列の場合は、次のように List<T> に変換します。
C#string json = """
[
{ "id": 1, "name": "Taro" },
{ "id": 2, "name": "Hanako" }
]
""";
List<User>? users = JsonSerializer.Deserialize<List<User>>(json);
4. C#でCSVをParserする方法
4-1. SplitでCSVを簡易的に解析する方法
単純なCSVであれば、Splitを使って解析できます。
C#string csvLine = "1,Taro,30";
string[] values = csvLine.Split(',');
var user = new User
{
Id = int.Parse(values[0]),
Name = values[1],
Age = int.Parse(values[2])
};
Console.WriteLine(user.Name);
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
ヘッダーなし、カンマを含まない、引用符も使わない、という単純なCSVならこの方法でも十分です。
4-2. Splitだけでは対応しにくいCSVの注意点
実務のCSVでは、次のようなデータがよく出てきます。
Id,Name,Note
1,"Yamada,Taro","Hello
World"
このCSVには、カンマを含むフィールド、引用符で囲まれたフィールド、改行を含むフィールドがあります。単純な Split(',') では正しく解析できません。
CSV Parserでは、次の点に注意が必要です。
| 注意点 | 内容 |
|---|---|
| カンマ入りデータ | "A,B" を1つの値として扱う |
| ダブルクォート | "" のようなエスケープに対応する |
| 改行入りデータ | フィールド内改行を扱う |
| 文字コード | UTF-8、Shift_JISなど |
| ヘッダー有無 | 1行目をデータとして扱うか |
| 空欄 | nullや空文字として扱うか |
実務でCSVを扱うなら、専用ライブラリを使う方が安全です。
4-3. CsvHelperを使ったCSV Parserの実装
C#でCSV Parserを実装するなら、CsvHelperがよく使われます。CsvHelperは、.NET向けのCSV読み書きライブラリで、クラスオブジェクトの読み書きにも対応しています。
基本的な読み込み例です。
C#using CsvHelper;
using System.Globalization;
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var records = csv.GetRecords<User>().ToList();
foreach (var user in records)
{
Console.WriteLine($"{user.Id}: {user.Name}");
}
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}
CSVファイルが次のような形式であれば、ヘッダー名とプロパティ名を対応付けて読み込めます。
Id,Name,Age
1,Taro,30
2,Hanako,25
4-4. CSVをクラスにマッピングする方法
CSVのヘッダー名とC#クラスのプロパティ名が異なる場合は、ClassMapを使ってマッピングできます。
C#using CsvHelper.Configuration;
public class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Name("user_id");
Map(m => m.Name).Name("user_name");
Map(m => m.Age).Name("age");
}
}
読み込み時にマップを登録します。
C#using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
csv.Context.RegisterClassMap<UserMap>();
var users = csv.GetRecords<User>().ToList();
CSVの列名が外部システム由来で自由に変えられない場合、この方法が便利です。
4-5. ヘッダーあり・ヘッダーなしCSVの読み込み
ヘッダーなしCSVを読み込む場合は、設定で HasHeaderRecord = false を指定します。
C#using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false
};
using var reader = new StreamReader("users.csv");
using var csv = new CsvReader(reader, config);
var records = csv.GetRecords<User>().ToList();
ヘッダーなしCSVでは、列の順番が重要です。そのため、ClassMapでインデックス指定をすると安全です。
C#public class UserMap : ClassMap<User>
{
public UserMap()
{
Map(m => m.Id).Index(0);
Map(m => m.Name).Index(1);
Map(m => m.Age).Index(2);
}
}
4-6. 文字コード・改行コード・カンマ入りデータへの対応
CSVを読み込むときは、文字コードにも注意が必要です。日本語環境では、UTF-8だけでなくShift_JISのCSVを扱うことがあります。
C#using System.Text;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
using var reader = new StreamReader("users.csv", Encoding.GetEncoding("shift_jis"));
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
var users = csv.GetRecords<User>().ToList();
改行コードは、WindowsではCRLF、Unix系ではLFが使われます。多くのCSVライブラリは一般的な改行コードに対応していますが、外部システム連携では事前にサンプルデータを確認しておくと安心です。
カンマ入りデータや引用符を含むデータは、SplitではなくCsvHelperのようなCSV Parserで処理しましょう。
5. C#で独自フォーマットをParserする方法
5-1. 独自ログや設定ファイルを解析する考え方
独自フォーマットのParserを作る場合、最初にやるべきことは「仕様を明確にする」ことです。
たとえば、次のようなログを解析するとします。
2026-06-07 10:30:15 [ERROR] File not found
このログの仕様は、次のように分解できます。
| 項目 | 内容 |
|---|---|
| 日時 | yyyy-MM-dd HH:mm:ss |
| レベル | [INFO]、[WARN]、[ERROR] |
| メッセージ | 行末までの文字列 |
Parserを作る前に、どこまでが日時で、どこからがレベルで、どこからがメッセージなのかを決めておく必要があります。
5-2. ルールベースでParserを設計する手順
独自Parserは、次の手順で設計すると整理しやすくなります。
入力フォーマットの例を集める
必須項目と任意項目を分ける
区切り文字やキーワードを決める
エラー時の扱いを決める
解析結果のクラスを作る
正常系・異常系のテストを作る
たとえば、設定ファイルを次のように定義します。
host=localhost
port=5432
ssl=true
解析結果のクラスは次のようになります。
C#public class AppSettings
{
public string Host { get; set; } = "";
public int Port { get; set; }
public bool Ssl { get; set; }
}
Parserでは、1行ずつ読み込み、=で分割し、キーに応じて値を変換します。
5-3. トークン分割と構文解析の基本
複雑なParserでは、いきなり入力全体を解析するのではなく、まずトークンに分割します。
たとえば、次のような数式を考えます。
1 + 2 * 3
これをトークンにすると、次のようになります。
[Number:1] [Plus:+] [Number:2] [Multiply:*] [Number:3]
トークン分割を行う処理をLexer、トークン列から意味のある構造を作る処理をParserと呼ぶことがあります。
単純な独自フォーマットなら Split や Regex で十分ですが、演算子の優先順位、括弧、入れ子構造がある場合は、本格的な構文解析が必要になります。
5-4. 正規表現を使った独自Parserの例
ログ文字列を正規表現でParserする例です。
C#using System.Text.RegularExpressions;
public record LogEntry(DateTime Timestamp, string Level, string Message);
public static LogEntry? ParseLog(string line)
{
var pattern = @"^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>[A-Z]+)\] (?<message>.*)$";
var match = Regex.Match(line, pattern);
if (!match.Success)
{
return null;
}
if (!DateTime.TryParse(match.Groups["timestamp"].Value, out var timestamp))
{
return null;
}
return new LogEntry(
timestamp,
match.Groups["level"].Value,
match.Groups["message"].Value
);
}
呼び出し例です。
C#var entry = ParseLog("2026-06-07 10:30:15 [INFO] Started");
if (entry is not null)
{
Console.WriteLine(entry.Level);
Console.WriteLine(entry.Message);
}
正規表現は、固定パターンの解析には強力です。ただし、仕様が複雑になりすぎると読みにくくなるため、無理に1つの正規表現で処理しないことも大切です。
5-5. SpracheやPidginを使ったParser Combinator入門
Parser Combinatorは、小さなParserを組み合わせて大きなParserを作る考え方です。C#では、SpracheやPidginのようなライブラリが知られています。
Pidginは、軽量で宣言的にParserを構築できるParser Combinatorライブラリとして説明されています。正規表現より広い範囲の言語を扱え、ANTLRのようなParser Generatorより手軽に使える場面があります。
たとえば、数値や識別子、演算子のような小さなParserを作り、それらを組み合わせて数式Parserを作るイメージです。
疑似的には次のような考え方になります。
C#// 数字を読むParser
// '+' を読むParser
// 数字 + 数字 の形を読むParser
Parser Combinatorは、次のようなケースに向いています。
| 向いているケース | 例 |
|---|---|
| 小さなDSL | 検索条件、フィルタ条件 |
| 設定式 | key:value AND status:open |
| 数式 | 1 + 2 * (3 + 4) |
| 正規表現では読みにくい構文 | 入れ子や再帰構造 |
5-6. 複雑な構文解析でライブラリを使うべき判断基準
独自Parserを自作するか、ライブラリを使うかは、次の基準で判断できます。
| 状況 | おすすめ |
|---|---|
| 単純な区切り文字だけ | Split |
| 固定パターンの文字列 | Regex |
| JSON | System.Text.Json / Newtonsoft.Json |
| CSV | CsvHelper |
| 小さなDSL | Sprache / Pidgin |
| 本格的な言語・複雑な文法 | ANTLR |
ANTLRは、構造化テキストやバイナリファイルを読み取り、処理・実行・変換するための強力なParser Generatorとして紹介されています。文法からParserを生成し、Parse Treeを構築できる点が特徴です。
文法が複雑で、将来的に仕様変更が多い場合は、手書きParserよりも専用ライブラリやParser Generatorを使う方が保守しやすくなります。
6. C# Parserにおすすめのライブラリ比較
6-1. System.Text.Json:標準機能でJSONを高速に解析
System.Text.Jsonは、C#でJSONを扱うときの第一候補です。標準機能として使いやすく、JsonSerializer.Deserialize<T>でJSON文字列をクラスに変換できます。
おすすめの用途は次のとおりです。
| 用途 | 内容 |
|---|---|
| Web APIレスポンス解析 | JSONをDTOに変換 |
| 設定データ読み込み | JSONファイルをクラス化 |
| 軽量なJSON処理 | 標準機能で完結 |
| 新規.NET開発 | 追加ライブラリを減らせる |
新規プロジェクトで特別な理由がなければ、まず System.Text.Json を検討するとよいでしょう。
6-2. Newtonsoft.Json:柔軟性の高い定番JSON Parser
Newtonsoft.Jsonは、長年多くのC#プロジェクトで使われてきたJSON Parserです。JObjectやJTokenを使った動的なJSON操作がしやすく、複雑な変換にも対応しやすいのが特徴です。
おすすめの用途は次のとおりです。
| 用途 | 内容 |
|---|---|
| 既存プロジェクト | すでに導入済みの場合 |
| 複雑なJSON変換 | カスタムConverterが必要 |
| 動的JSON操作 | JObjectで柔軟に扱う |
| 古い環境 | 標準JSON機能だけでは不足する場合 |
System.Text.Jsonで対応しにくい要件がある場合に検討するとよいでしょう。
6-3. CsvHelper:CSV解析に強い実用的なライブラリ
CsvHelperは、C#でCSVを読み書きするための実用的なライブラリです。ヘッダー付きCSV、クラスマッピング、型変換、カルチャ指定など、実務で必要になりやすい機能を備えています。
おすすめの用途は次のとおりです。
| 用途 | 内容 |
|---|---|
| 業務CSV取り込み | 売上、顧客、在庫データ |
| 外部システム連携 | CSVエクスポート・インポート |
| ヘッダー付きCSV | プロパティに自動マッピング |
| 複雑なCSV | カンマ入り、引用符、改行対応 |
CSVを本格的に扱うなら、SplitよりCsvHelperを使う方が安全です。
6-4. Sprache:読みやすく書けるParser Combinator
Spracheは、C#でParser Combinatorを実装するためのライブラリです。小さなParserを組み合わせて、読みやすい構文解析処理を作ることができます。
おすすめの用途は次のとおりです。
| 用途 | 内容 |
|---|---|
| 小規模DSL | 条件式、検索式 |
| 学習用途 | Parser Combinatorの理解 |
| 手軽な構文解析 | 大がかりな生成ツールを使いたくない場合 |
複雑すぎない構文を、C#コードとして自然に表現したい場合に向いています。
6-5. Pidgin:パフォーマンスを意識したParser Combinator
PidginもC#向けのParser Combinatorライブラリです。軽量で高速なParserを宣言的に作れる点が特徴です。
おすすめの用途は次のとおりです。
| 用途 | 内容 |
|---|---|
| 数式Parser | 演算子や括弧を扱う |
| DSL Parser | 独自ルールの解析 |
| 正規表現の限界を超える解析 | 入れ子構造を扱う |
| 生成ツールなしのParser | C#コードだけで完結させる |
正規表現では複雑すぎるが、ANTLRを使うほど大規模ではない場合に適しています。
6-6. ANTLR:本格的な構文解析に向いたParser Generator
ANTLRは、文法ファイルからLexerやParserを生成する本格的なParser Generatorです。独自言語、DSL、SQL風構文、コード解析ツールなど、複雑な構文解析に向いています。
おすすめの用途は次のとおりです。
| 用途 | 内容 |
|---|---|
| 独自言語 | DSLやスクリプト言語 |
| 複雑な文法 | 入れ子、優先順位、再帰 |
| 静的解析 | ソースコード解析 |
| 長期保守 | 文法を明示的に管理したい場合 |
ANTLRは強力ですが、文法ファイルや生成コードの理解が必要です。小さな文字列解析には過剰になることもあります。
6-7. 用途別おすすめライブラリ早見表
| 目的 | おすすめ |
|---|---|
| 数値・日付変換 | TryParse |
| 単純な文字列分割 | Split |
| 固定パターン抽出 | Regex |
| JSON解析 | System.Text.Json |
| 柔軟なJSON操作 | Newtonsoft.Json |
| CSV解析 | CsvHelper |
| 小規模DSL解析 | Sprache / Pidgin |
| 本格的な構文解析 | ANTLR |
C# Parserでは、すべてを自作しようとするのではなく、解析対象に合った標準機能やライブラリを選ぶことが重要です。
7. C# Parserの実装例
7-1. 数値文字列を安全に解析するサンプル
C#public static int? ParseIntOrNull(string? input)
{
if (string.IsNullOrWhiteSpace(input))
{
return null;
}
return int.TryParse(input, out int value) ? value : null;
}
使用例です。
C#int? value = ParseIntOrNull("123");
if (value.HasValue)
{
Console.WriteLine(value.Value);
}
else
{
Console.WriteLine("整数として解析できませんでした。");
}
戻り値を int? にすると、解析失敗を自然に表現できます。
7-2. 日付文字列をDateTimeに変換するサンプル
日付フォーマットが決まっている場合は、DateTime.TryParseExactを使うと安全です。
C#using System.Globalization;
public static DateTime? ParseDate(string? input)
{
if (string.IsNullOrWhiteSpace(input))
{
return null;
}
string format = "yyyy-MM-dd";
bool success = DateTime.TryParseExact(
input,
format,
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime date
);
return success ? date : null;
}
TryParseExactは、指定した形式以外を受け付けたくない場合に便利です。
7-3. JSON文字列をクラスに変換するサンプル
C#using System.Text.Json;
public static User? ParseUserJson(string json)
{
try
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<User>(json, options);
}
catch (JsonException)
{
return null;
}
}
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
}
呼び出し例です。
C#string json = """{ "id": 1, "name": "Taro" }""";
User? user = ParseUserJson(json);
if (user is not null)
{
Console.WriteLine(user.Name);
}
7-4. CSVファイルを読み込んで一覧化するサンプル
C#using CsvHelper;
using System.Globalization;
public static List<User> LoadUsersFromCsv(string path)
{
using var reader = new StreamReader(path);
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
return csv.GetRecords<User>().ToList();
}
呼び出し例です。
C#var users = LoadUsersFromCsv("users.csv");
foreach (var user in users)
{
Console.WriteLine($"{user.Id}: {user.Name}");
}
CSVの仕様が単純でない場合は、ClassMapや設定オプションを追加しましょう。
7-5. ログ文字列から日時・レベル・メッセージを抽出するサンプル
C#using System.Text.RegularExpressions;
public record LogEntry(DateTime Timestamp, string Level, string Message);
public static bool TryParseLog(string line, out LogEntry? entry)
{
entry = null;
var match = Regex.Match(
line,
@"^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>[A-Z]+)\] (?<message>.+)$"
);
if (!match.Success)
{
return false;
}
if (!DateTime.TryParse(match.Groups["timestamp"].Value, out var timestamp))
{
return false;
}
entry = new LogEntry(
timestamp,
match.Groups["level"].Value,
match.Groups["message"].Value
);
return true;
}
呼び出し例です。
C#if (TryParseLog("2026-06-07 10:30:15 [ERROR] File not found", out var log))
{
Console.WriteLine(log!.Timestamp);
Console.WriteLine(log.Level);
Console.WriteLine(log.Message);
}
7-6. 入力値の検証とエラーハンドリングを含めた実装例
実務では、単に null を返すだけでなく、エラーメッセージも返したい場合があります。
C#public class ParseResult<T>
{
public bool Success { get; init; }
public T? Value { get; init; }
public string? ErrorMessage { get; init; }
public static ParseResult<T> Ok(T value) => new()
{
Success = true,
Value = value
};
public static ParseResult<T> Error(string message) => new()
{
Success = false,
ErrorMessage = message
};
}
数値Parserに適用します。
C#public static ParseResult<int> ParseAge(string? input)
{
if (string.IsNullOrWhiteSpace(input))
{
return ParseResult<int>.Error("年齢が入力されていません。");
}
if (!int.TryParse(input, out int age))
{
return ParseResult<int>.Error("年齢は整数で入力してください。");
}
if (age < 0 || age > 150)
{
return ParseResult<int>.Error("年齢の範囲が不正です。");
}
return ParseResult<int>.Ok(age);
}
このようにすると、Parserの失敗理由を画面表示やログ出力に利用できます。
8. C# Parser実装で失敗しやすいポイント
8-1. Parseで例外が発生する原因
Parseで例外が発生する主な原因は、入力形式の不正、空文字、null、範囲外の値です。
C#int.Parse("abc"); // 形式不正
int.Parse(""); // 空文字
int.Parse("9999999999"); // intの範囲外
外部入力には TryParse を使いましょう。
C#if (!int.TryParse(input, out int value))
{
Console.WriteLine("整数として解析できません。");
}
8-2. nullや空文字への対応漏れ
Parserでは、nullや空文字の扱いを最初に決める必要があります。
C#if (string.IsNullOrWhiteSpace(input))
{
return null;
}
空文字をエラーにするのか、既定値として扱うのか、nullとして扱うのかを曖昧にすると、後続処理で不具合が起きやすくなります。
8-3. カルチャ設定による数値・日付解析の違い
数値や日付のParserでは、カルチャの違いに注意が必要です。
たとえば、小数点が . の文化圏もあれば、, を使う文化圏もあります。日付も yyyy-MM-dd、MM/dd/yyyy、dd/MM/yyyy など複数の形式があります。
固定フォーマットのデータを扱う場合は、CultureInfo.InvariantCultureやTryParseExactを使うと安定します。
C#using System.Globalization;
decimal.TryParse(
"123.45",
NumberStyles.Number,
CultureInfo.InvariantCulture,
out decimal value
);
8-4. 文字コードや改行コードの違い
ファイルParserでは、文字コードと改行コードの違いも重要です。
日本語CSVでは、UTF-8だけでなくShift_JISが使われることがあります。文字化けする場合は、読み込み時のEncodingを確認しましょう。
C#Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
using var reader = new StreamReader(
"data.csv",
Encoding.GetEncoding("shift_jis")
);
改行コードも、WindowsのCRLF、LinuxやmacOSのLFが混在することがあります。行単位で処理するParserでは、サンプルデータを複数用意してテストしましょう。
8-5. 想定外フォーマットへの耐性不足
Parserは、想定どおりの入力だけでなく、想定外の入力にも対応する必要があります。
たとえば、CSVの列数が足りない場合です。
C#string[] parts = line.Split(',');
if (parts.Length < 3)
{
Console.WriteLine("列数が不足しています。");
return;
}
JSONでプロパティが存在しない場合も同様です。
C#if (root.TryGetProperty("name", out var nameElement))
{
Console.WriteLine(nameElement.GetString());
}
外部入力を扱うParserでは、「壊れたデータが来る可能性がある」という前提で実装しましょう。
8-6. 大容量データ解析時のパフォーマンス問題
大容量データをParserする場合、すべてをメモリに読み込むとパフォーマンス問題が発生します。
避けたい例です。
C#string text = File.ReadAllText("large.csv");
大きなファイルでは、ストリームや行単位の処理を使いましょう。
C#foreach (var line in File.ReadLines("large.csv"))
{
// 1行ずつ解析する
}
JSONでも、巨大な配列を一括で List<T> に変換するとメモリ消費が大きくなります。大容量データでは、ストリーミング処理や分割処理を検討しましょう。
9. C# Parserを設計するときのベストプラクティス
9-1. 解析対象の仕様を明確にする
Parserを作る前に、入力仕様を明確にしましょう。
決めるべき項目は次のとおりです。
| 項目 | 例 |
|---|---|
| 入力形式 | JSON、CSV、ログ、独自形式 |
| 必須項目 | ID、日時、名前 |
| 任意項目 | メモ、説明文 |
| 区切り文字 | カンマ、タブ、スペース |
| 文字コード | UTF-8、Shift_JIS |
| エラー時の扱い | スキップ、例外、エラー一覧に追加 |
仕様が曖昧なままParserを書くと、後から例外処理や分岐が増えて保守しにくくなります。
9-2. TryParseやバリデーションで安全性を高める
外部入力を扱う場合は、ParseよりTryParseを優先しましょう。
C#if (!DateTime.TryParse(input, out var date))
{
return ParseResult<DateTime>.Error("日付形式が不正です。");
}
また、型変換だけでなく、業務ルールの検証も必要です。
C#if (price < 0)
{
return ParseResult<decimal>.Error("価格は0以上である必要があります。");
}
Parserとバリデーションを適切に組み合わせることで、後続処理を安全にできます。
9-3. 例外処理とエラーメッセージを分かりやすくする
Parserのエラーメッセージは、利用者や開発者が原因を特定しやすい内容にしましょう。
悪い例です。
解析エラー
良い例です。
3行目のAge列が整数ではありません。入力値: abc
CSVやログのParserでは、行番号、列名、入力値を含めるとデバッグしやすくなります。
9-4. テストしやすいParserクラスを作る
Parserは、ファイル読み込み処理と解析処理を分けるとテストしやすくなります。
C#public class UserCsvParser
{
public User ParseLine(string line)
{
var parts = line.Split(',');
return new User
{
Id = int.Parse(parts[0]),
Name = parts[1],
Age = int.Parse(parts[2])
};
}
}
ファイルを直接読むメソッドだけにすると、単体テストが面倒になります。文字列を受け取って解析するメソッドを用意しておくと、正常系・異常系のテストを書きやすくなります。
9-5. 小さなParserを組み合わせて保守性を高める
複雑なParserを1つのメソッドに詰め込むと、保守が難しくなります。
たとえば、ログParserなら次のように分割できます。
| メソッド | 役割 |
|---|---|
| ParseTimestamp | 日時を解析 |
| ParseLevel | ログレベルを解析 |
| ParseMessage | メッセージを解析 |
| ParseLine | 全体を組み立てる |
小さなParserを組み合わせると、テストしやすく、仕様変更にも対応しやすくなります。
9-6. ライブラリと自作Parserの使い分け
C# Parserでは、自作するかライブラリを使うかの判断が重要です。
| 条件 | 判断 |
|---|---|
| 仕様が単純 | 自作で十分 |
| 標準形式 | 標準機能や定番ライブラリを使う |
| 仕様が複雑 | 専用ライブラリを検討 |
| 将来変更が多い | 保守しやすい設計を優先 |
| セキュリティが重要 | 実績あるライブラリを使う |
JSONやCSVのような一般的な形式は、自作Parserよりも実績あるライブラリを使う方が安全です。一方、単純なログや固定フォーマットなら、自作Parserでも十分対応できます。
10. C# Parserに関するよくある質問
10-1. C#でParserを自作するべきですか?
解析対象が単純であれば自作しても問題ありません。たとえば、固定形式のログや key=value 形式の設定ファイルなら、SplitやRegexで十分な場合があります。
ただし、JSON、CSV、XML、プログラミング言語風の構文など、仕様が複雑な形式は自作しない方が安全です。特にCSVは一見簡単に見えますが、引用符、改行、カンマ入りデータなどを正しく扱う必要があります。
10-2. ParseとTryParseはどちらを使うべきですか?
外部入力を扱うなら、基本的には TryParse を使うべきです。
Parseは、入力が必ず正しいと保証できる場合に向いています。たとえば、プログラム内部で生成した文字列を変換するだけなら Parse でも問題ありません。
一方、ユーザー入力、CSV、ログ、APIレスポンスなどは不正な値が含まれる可能性があるため、TryParseで安全に解析しましょう。
10-3. JSON ParserはSystem.Text.JsonとNewtonsoft.Jsonのどちらがおすすめですか?
新規開発では、まず System.Text.Json をおすすめします。標準機能として使いやすく、一般的なJSON解析には十分対応できます。
一方、既存プロジェクトで Newtonsoft.Json を使っている場合や、JObject、JTokenによる柔軟な動的操作、複雑なカスタム変換が必要な場合は、Newtonsoft.Json が適しています。
迷った場合は、標準機能で実現できるかを確認し、不足があれば Newtonsoft.Json を検討するとよいでしょう。
10-4. CSV解析にSplitを使っても問題ありませんか?
単純なCSVであれば Split でも問題ありません。
たとえば、次のような条件をすべて満たす場合です。
| 条件 | 内容 |
|---|---|
| カンマ入りデータがない | "Yamada,Taro" のような値がない |
| 引用符がない | ダブルクォートを使わない |
| 改行入りデータがない | 1レコードが必ず1行 |
| 列数が固定 | 毎回同じ列数 |
しかし、実務のCSVではこれらの条件を満たさないことが多いため、CsvHelperのようなCSV Parserを使うのがおすすめです。
10-5. 正規表現Parserと専用ライブラリはどう使い分けますか?
固定パターンの文字列解析には正規表現が向いています。ログ、ID、メールアドレス、簡単なコマンド文字列などです。
一方、入れ子構造、演算子の優先順位、再帰的な文法、複数ルールの組み合わせが必要な場合は、正規表現だけでは読みにくくなります。その場合は、Parser CombinatorやANTLRのような専用ライブラリを使うと保守しやすくなります。
10-6. 大量データをParserする場合の注意点は何ですか?
大量データをParserする場合は、メモリ使用量と処理時間に注意が必要です。
ファイル全体を一括で読み込むのではなく、ストリームや行単位で処理しましょう。
C#foreach (var line in File.ReadLines("large.log"))
{
// 1行ずつParserに渡す
}
また、エラーが発生したときに全処理を止めるのか、エラー行だけスキップするのかも事前に決めておくべきです。
大量データでは、次の点を意識しましょう。
| ポイント | 内容 |
|---|---|
| ストリーミング | 一括読み込みを避ける |
| エラー処理 | 失敗行を記録する |
| ログ出力 | 行番号や原因を残す |
| 並列処理 | 必要に応じて検討 |
| ライブラリ選定 | 大容量対応を確認する |
まとめ
C# Parserは、文字列、JSON、CSV、ログ、独自フォーマットなど、さまざまな入力データをC#で扱いやすい形に変換するための重要な技術です。
数値や日付の変換では、ParseよりもTryParseを使うことで安全性を高められます。単純な文字列解析にはSplit、固定パターンの抽出にはRegexが便利です。
JSONをParserする場合は、まず標準のSystem.Text.Jsonを検討し、柔軟な動的操作や既存資産との互換性が必要な場合はNewtonsoft.Jsonを使うとよいでしょう。
CSV解析では、単純なデータならSplitでも対応できますが、実務ではカンマ入りデータ、引用符、改行、文字コードなどの問題があるため、CsvHelperのような専用ライブラリを使うのが安全です。
独自フォーマットをParserする場合は、まず仕様を明確にし、単純なものはSplitやRegex、複雑なものはSprache、Pidgin、ANTLRなどを検討しましょう。
C# Parserを設計するときは、入力仕様、エラー処理、テストしやすさ、保守性、パフォーマンスを意識することが大切です。解析対象に合った方法を選べば、安全で読みやすく、変更に強いParserを実装できます。

