「C Sharpの基礎 - CSVファイル」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
71行目: 71行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
==== CSVファイルを読み込む ====
==== CSVファイルの読み込み ====
===== エンコーディングの指定 =====
<code>TextFieldParser</code>クラスを使用してCSVファイルを読み込むには、<br>
<code>TextFieldParser</code>クラスを使用してCSVファイルを読み込むには、<br>
まず、<code>TextFieldParser</code>クラスのコンストラクタに処理するCSVファイルを指定して、インスタンスを生成する。<br>
まず、<code>TextFieldParser</code>クラスのコンストラクタに処理するCSVファイルを指定して、インスタンスを生成する。<br>
<br>
CSVファイルに日本語が含まれている場合は、文字コードを指定する。<br>
CSVファイルに日本語が含まれている場合は、文字コードを指定する。<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
79行目: 81行目:
  using Microsoft.VisualBasic.FileIO;
  using Microsoft.VisualBasic.FileIO;
   
   
  var parser = new TextFieldParser(@"hoge.csv", Encoding.GetEncoding("Shift_JIS"));  // 文字コードはShift-JISを指定する
  var parser = new TextFieldParser(@"hoge.csv", Encoding.GetEncoding("Shift_JIS"));  // Shift-JISを指定
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
次に、<code>TextFieldType</code>プロパティに<code>FieldType.Delimited</code>を指定する。<br>
===== デリミタの指定 =====
もし、<code>FieldType.FixedWidth</code>を指定する場合、フィールドが固定幅のファイルも扱うことができる。<br>
デリミタを指定する場合は、<code>TextFieldType</code>プロパティに<code>FieldType.Delimited</code>を指定する。<br>
<br>
<br>
また、<code>SetDelimiters</code>メソッドを使用して、区切り文字を指定する。(複数の区切り文字が指定可能)<br>
<code>SetDelimiters</code>メソッドを使用して、区切り文字を指定する。(複数の区切り文字が指定可能)<br>
CSVファイルの場合は、<code>,</code>(カンマ)を指定する。<br>
CSVファイルの場合は、<code>,</code>(カンマ)を指定する。<br>
<br>
また、<code>FieldType.FixedWidth</code>を指定する場合、フィールドが固定幅のファイルも扱うことができる。<br>
<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
  parser.TextFieldType = FieldType.Delimited;
  parser.TextFieldType = FieldType.Delimited;
92行目: 97行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
===== レコードの取得 ====
<code>TextFieldParser</code>クラスの<code>ReadFields</code>メソッドを実行するごとに、CSVファイルを1行ずつ読むことができる。<br>
<code>TextFieldParser</code>クラスの<code>ReadFields</code>メソッドを実行するごとに、CSVファイルを1行ずつ読むことができる。<br>
<code>ReadFields</code>メソッドは、読み込んだレコードの全てのフィールドを文字列配列に変換して返す。<br>
<code>ReadFields</code>メソッドは、読み込んだレコードの全てのフィールドを文字列配列に変換して返す。<br>
次のレコードが存在するかどうかは<code>EndOfData</code>プロパティにより判定できるため、ループにより、CSVファイル全体を処理できる。<br>
<br>
次のレコードが存在するかどうかは<code>EndOfData</code>プロパティにより判定できるため、繰り返し文を使用してCSVファイル全体を処理する。<br>
  <syntaxhighlight lang="c#">
  <syntaxhighlight lang="c#">
  while (!parser.EndOfData)
  while (!parser.EndOfData)
103行目: 110行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
==== CSVファイルを読み込み表示する ====
==== CSVファイルを読み込み表示する ====
以下の例では、hoge.csvファイルを読み込み、各フィールドを切り出してタブ区切りで画面に出力する。<br>
以下の例では、hoge.csvファイルを読み込み、各フィールドを切り出してタブ区切りで画面に出力する。<br>

2024年9月14日 (土) 22:40時点における版

概要

CSV (Comma-Separated Values) は、テキストベースのシンプルなファイル形式である。
各行がデータレコードを表しており、フィールドはカンマ (または他の区切り文字) で区切られている。

C#でCSVファイルを読み込む場合は、主にSystem.IO名前空間のクラスを使用する。
File.ReadAllLinesメソッドを使用してファイルの全行を文字列配列として読み込み、次に、String.Splitメソッドで各行をフィールドに分割する。

CSVファイルへ書き込む場合は、StreamWriterクラスを使用して行うことができる。
各フィールドをカンマで連結して、WriteLineメソッドで1行ずつファイルに書き込む。

より複雑なCSV操作には、サードパーティのライブラリを利用することも可能である。
CsvHelperライブラリは人気のあるライブラリであり、読み書きの柔軟性が高く、大規模なCSVファイルの処理に適している。

CSVファイルを扱う時の注意点として、フィールド内にカンマが含まれる場合の処理がある。
このような場合、フィールドを引用符で囲むなどの対策が必要である。

また、文字エンコーディングの問題にも注意が必要であり、特に異なる言語や地域のデータを扱う場合は重要である。

C#のLINQを活用すると、CSVデータの効率的な操作や分析が可能になる。
例えば、特定の条件に合うレコードのフィルタリング、あるいは、データの集計等を簡単に行うことができる。

セキュリティの観点からは、外部ソースからのCSVファイルを扱う場合には注意が必要である。
悪意のあるデータが含まれている可能性があるため、適切な入力検証とサニタイズを行うことが重要である。

パフォーマンスを考慮する場合、大規模なCSVファイルを扱う場合はストリーミング読み取りを検討する。
これにより、メモリ使用量を抑えつつ効率的に処理を行うことができる。


TextFieldParserクラス

CSVファイルを読み込むには、.NET Framework 2.0で追加されたTextFieldParserクラス(Microsoft.VisualBasic.FileIO名前空間)を使用する。
TextFieldParserクラスを使用することで、CSVファイルを読み込み、各行の各フィールドの文字列を簡潔に取得することができる。

RFC 4180との関係

TextFieldParserクラスは多くのRFC 4180の要件を満たしている。 ただし、いくつかの点で完全な準拠ではない。

  • 準拠している点
    • カンマで区切られたフィールドの処理
    • 引用符で囲まれたフィールド内のカンマの処理
    • 改行文字を含むフィールドの処理

  • 部分的に準拠している点
    • 引用符の扱い
      TextFieldParserクラスは引用符の使用を柔軟に設定できるが、デフォルトの動作がRFC 4180と完全に一致するわけではない。

  • 準拠していない可能性がある点
    • ヘッダ行の特別な扱い
      RFC 4180はヘッダ行について言及しているが、TextFieldParserクラスにはヘッダ行を自動的に識別する機能がない。
    • 厳密なCRLF改行の要求
      RFC 4180はCRLF (\r\n) を改行として指定しているが、TextFieldParserクラスはより柔軟である。


以下の例では、TextFieldParserクラスを使用して、RFC 4180の仕様に近づけている。

 using Microsoft.VisualBasic.FileIO;
 
 using (TextFieldParser parser = new TextFieldParser("sample.csv"))
 {
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    parser.HasFieldsEnclosedInQuotes = true;  // RFC 4180に準拠するため
 
    // ヘッダ行の処理
    string[] headers = parser.ReadFields();
 
    while (!parser.EndOfData)
    {
       string[] fields = parser.ReadFields();
       // フィールドの処理を行う
    }
 }


CSVファイルの読み込み

エンコーディングの指定

TextFieldParserクラスを使用してCSVファイルを読み込むには、
まず、TextFieldParserクラスのコンストラクタに処理するCSVファイルを指定して、インスタンスを生成する。

CSVファイルに日本語が含まれている場合は、文字コードを指定する。

 using System.Text;
 using Microsoft.VisualBasic.FileIO;
 
 var parser = new TextFieldParser(@"hoge.csv", Encoding.GetEncoding("Shift_JIS"));  // Shift-JISを指定


デリミタの指定

デリミタを指定する場合は、TextFieldTypeプロパティにFieldType.Delimitedを指定する。

SetDelimitersメソッドを使用して、区切り文字を指定する。(複数の区切り文字が指定可能)
CSVファイルの場合は、,(カンマ)を指定する。

また、FieldType.FixedWidthを指定する場合、フィールドが固定幅のファイルも扱うことができる。

 parser.TextFieldType = FieldType.Delimited;
 parser.SetDelimiters(",");  // 区切り文字はカンマを指定する


= レコードの取得

TextFieldParserクラスのReadFieldsメソッドを実行するごとに、CSVファイルを1行ずつ読むことができる。
ReadFieldsメソッドは、読み込んだレコードの全てのフィールドを文字列配列に変換して返す。

次のレコードが存在するかどうかはEndOfDataプロパティにより判定できるため、繰り返し文を使用してCSVファイル全体を処理する。

 while (!parser.EndOfData)
 {
     // 配列rowの要素は読み込んだレコードの各フィールドの値
     string[] row = parser.ReadFields();  // 1レコード読み込む
 }


CSVファイルを読み込み表示する

以下の例では、hoge.csvファイルを読み込み、各フィールドを切り出してタブ区切りで画面に出力する。
改行文字および空白文字がどのように処理されるかを分かりやすくするために、それぞれをnと_に置換して出力している。

  • HasFieldsEnclosedInQuotesプロパティ
    フィールドに改行やデリミタを含める為に引用符を使っているようなフォーマットを考慮する場合は、
    HasFieldsEnclosedInQuotesプロパティをtrueに設定する。(初期値はtrue)
  • TrimWhiteSpaceプロパティ
    フィールドの前後の空白文字を削除しない場合は、TrimWhiteSpaceプロパティをfalseに設定する。(初期値はtrue)


 using System;
 using System.Text;
 using Microsoft.VisualBasic.FileIO;
 
 class CSVParser
 {
    static void Main()
    {
       using (var parser = new TextFieldParser("text.csv", Encoding.GetEncoding("Shift_JIS")))
       {
          parser.TextFieldType = FieldType.Delimited;  // フィールドはデリミタにより区切る(可変)
          parser.Delimiters = new[] {","};             // 区切り文字を指定
          parser.CommentTokens = new[] {"#"};          // #で始まる行をコメントとする
 
          // parser.HasFieldsEnclosedInQuotes = false; // 引用符で括られたフィールドを持つか指定
          // parser.TrimWhiteSpace = false;            // フィールドの前後に含まれる空白を削除するか指定
 
          while (!parser.EndOfData)
          {
             try
             {
                string[] row = parser.ReadFields(); // 1行読み込む
                foreach (string field in row)
                {
                   field = field.Replace("\r\n", "n"); // 改行をnに置換
                   field = field.Replace(" ", "_");    // 空白を_に置換
                   Console.Write(field + "\t");    // タブ区切りで出力
                }
                Console.WriteLine();
             }
             catch(Exception ex)
             {
                Console.WriteLine(ex.Message);
             }
          }
       }
    }
 }