うぃろぅ.log

140字で綴りきれない日々の徒然備忘録

【C#】 CSVのヘッダーだけ読み込んで出力する

うぃろぅです。

C#の新しい案件を進めているのはいいものの、どことなーくレガシーな気配が漂っている現状です。

まぁ嘆いていても始まらないので業務をこなすわけですが、その際に「あーこれしないとな、でも手作業だと面倒だからツール作ろう」と作った備忘録。

何がしたいか

date,time,foo,bar,baz
2019/2/25,00:00,1,2,3
2019/2/25,00:05,4,5,6
2019/2/25,00:10,7,8,9
  .
  .
  .

上記のようなCSVファイルがあるとして、これを

date
time
foo
bar
baz

こうしたい。なんて簡単な。

何でこんなことをしたくなったかというと現新比較がしたかったから。

  • 現行フォーマットで出力されているCSVがある
    (dateTime,hoge,hugaみたいな感じで結構違う)
  • 新フォーマットで出力されるように変更する
  • 現行フォーマットのCSVVBAで取り込んで処理を行っているため、新フォーマットだと動かない
  • VBAは10年くらい前に当時の作業者が片手間で作ったため読み解くのにコストがかかる
  • よって新フォーマットのCSVを現行フォーマットに変換したい

みたいな流れ。その際に変換リストの上から順番に出力されるようにしたら順番が違っている。
なのでWinMergeのようなDiffソフトで比較して修正するのが手っ取り早いよね、というワケ。

そもそもツールが動かない場合に頑張って解析しなきゃいけないとか、もう使わないファイルもあるけれどファイル名がないとエラーが出るからダミーファイルに置き換えるとか、もうそれ新フォーマット取り込む新しいツール作成した方がいいのでは、なんて突っ込みは野暮というもの。予算が悪いよ予算が。

ここ1ヶ月くらいでやったことは、

  • どの項目が現新で対応しているかのマッピング
  • マッピング結果をXMLファイルで作成
  • 新フォーマットのCSVを取り込み現行フォーマットに変換して出力するツール作成

ってところ。DBは使わないらしい。うーんレガシー。まあ管理コストかからないからこれはこれでありか。…いや、ありか??

ちなみにこのCSVファイル、150種類くらいある。XMLファイル作るのめっちゃ大変だった。

閑話休題。以下ソース。

using System.Text;
using System.IO;
using Microsoft.VisualBasic.FileIO;

static void Main(string[] args)
{
  // Shift-JISで出力されるので合わせる
  var sJis = Encoding.GetEncoding("Shift-JIS");

  var path = @"C:\Users\sample\csv";
  var csvs = Directory.GetFiles(path);

  // 読み込んだファイル数分ループ
  foreach (var csv in csvs)
  {
    // CSV読み込み
    var parser = new TextFieldParser(csv, sJis))
    {
      TextFieldType = FieldType.Delimited,
      Delimiters = new string[] { "," }
    };

    // ヘッダー読み込み
    var heads = parser.ReadFields();

    // 出力先設定
    var outPath = Path.Combine(
      path, "header", Path.GetFileName(csv));

    // 出力
    using (var sw = new StreamWriter(outPath, false, sJis))
    {
      foreach(var head in heads)
      {
        sw.WriteLine(head);
      }
    }
  }
}

かんたーん!

CSV読み込みにはTextFieldParserを利用。
Microsoft.VisualBasicを参照に追加する必要があるためそこだけ注意。

CSVの格納フォルダを決め打ちしているが、configファイルから読み込むようにすればリビルド不要。

System.Configuration.ConfigurationManager.AppSettings["inputCsvPath"];みたいな感じか。

最適化とか全然していないけれど、これで動きそうだしいいかと。
サブ作業に時間かけてもしょうがないし。将来の自分へのメモ。
役に立つかは知らない。多分立たないかなぁ。

ではまた。