状況
「このCSVファイルのココとココをちょっと変更して。簡単でしょ。30分で」という依頼をうっかり引き受けてみたら、自由なルールの CSV のために手こずったので、備忘録もかねて記載します。
ちなみに、受け取った CSV はこんな内容でした。
1,"029","奈良県","ようこそ!",2021/2/10
2,"013","東京都","本日の面会者は、
・山田
・田中
・大谷",2021/3/10
単純なカンマ区切りだけかと思っていたら次の様なルールがありました。
- 数値扱いの項目はダブルコーテーションがない
- 文字列扱いの項目はダブルコーテーションで囲まれている
- 前ゼロの桁長固定の項目がある
- 改行が含まれる文字列の項目がある
sed や awk といったツールは?
考えましたが、改行を含む項目は技術力が足りずにうまく処理ができなかったので、早々にあきらめました。
Excel は CSV の天敵だと思っているので試していません。
C# テンプレ
作った方が早いじゃん、という人のために。
C# と CSV では定番の CSVHelper を使った、CSV をコンバートする際の汎用的なテンプレートを作成しました。CSVHelper は、CSV の読み込み、書込みの支援ライブラリで、ダブルコーテーションのあるなし、改行を含む文字列などにも対応しています。
汎用ということで、項目数は100項目までなら読み込めます。100以上あったらCSVData クラスに格納用プロパティを追加するだけでOKです。
ちなみに、CSVHelper の使い方については 「CSVファイルを扱うのに便利な CSVHelper ver 25.0 の Getting Start」をどうぞ!
使い方
まずはパッケージマネージャーで CSVHelper をインストールします。
PM> install-package csvhelper
次に下のコードを、うまく自分の Program.cs にコピーしていってください。
中央にある「//ここに変更処理を書きます!」の部分で、読み込んだ CSV データが格納されている CSVDataList リストの中身をいじくります。
読み込み時や書き込み時に処理した方がいいこともあるので、臨機応変にコードを加工します。
実行時
実際の使用ですが、実行時に第1引数に元 CSV ファイル名、第2引数にコンバート後の CSV ファイル名を指定します。
コード
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;
namespace csvconvertsample
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 2)
{
Console.Write(@"このコマンドは、第1引数に元のCSVファイル名を、第2引数にコンバート先CSVファイル名を指定します。");
return;
}
if (!File.Exists(args[0]))
{
Console.Write($@"第1引数に指定した元CSVファイルが存在しません:{args[0]}");
return;
}
if (File.Exists(args[1]))
{
Console.Write($@"第2引数に指定した先CSVファイルがすでに存在します:{args[1]}");
return;
}
List<CSVData> CSVDataList = new List<CSVData>();
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
MissingFieldFound = (a) =>{},
};
try
{
using (var reader = new StreamReader(args[0], Encoding.GetEncoding("Shift_JIS")))
{
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<CSVData>();
foreach (var r in records)
{
CSVDataList.Add(r);
}
}
}
}
catch (Exception err)
{
Console.Write($@"次のエラーが発生しました:{err.Message}");
return;
}
//ここに変更処理を書きます!
try
{
using (var writer = new StreamWriter(args[1], false, Encoding.GetEncoding("SHIFT_JIS")))
{
using (var csv = new CsvWriter(writer, config))
{
csv.WriteRecords(CSVDataList);
}
}
}
catch (Exception err)
{
Console.Write($@"次のエラーが発生しました:{err.Message}");
return;
}
}
}
public class CSVData
{
[Index(0)]
public string col001 { get; set; }
[Index(1)]
public string col002 { get; set; }
[Index(2)]
public string col003 { get; set; }
[Index(3)]
public string col004 { get; set; }
[Index(4)]
public string col005 { get; set; }
[Index(5)]
public string col006 { get; set; }
[Index(6)]
public string col007 { get; set; }
[Index(7)]
public string col008 { get; set; }
[Index(8)]
public string col009 { get; set; }
[Index(9)]
public string col010 { get; set; }
[Index(10)]
public string col011 { get; set; }
[Index(11)]
public string col012 { get; set; }
[Index(12)]
public string col013 { get; set; }
[Index(13)]
public string col014 { get; set; }
[Index(14)]
public string col015 { get; set; }
[Index(15)]
public string col016 { get; set; }
[Index(16)]
public string col017 { get; set; }
[Index(17)]
public string col018 { get; set; }
[Index(18)]
public string col019 { get; set; }
[Index(19)]
public string col020 { get; set; }
[Index(20)]
public string col021 { get; set; }
[Index(21)]
public string col022 { get; set; }
[Index(22)]
public string col023 { get; set; }
[Index(23)]
public string col024 { get; set; }
[Index(24)]
public string col025 { get; set; }
[Index(25)]
public string col026 { get; set; }
[Index(26)]
public string col027 { get; set; }
[Index(27)]
public string col028 { get; set; }
[Index(28)]
public string col029 { get; set; }
[Index(29)]
public string col030 { get; set; }
[Index(30)]
public string col031 { get; set; }
[Index(31)]
public string col032 { get; set; }
[Index(32)]
public string col033 { get; set; }
[Index(33)]
public string col034 { get; set; }
[Index(34)]
public string col035 { get; set; }
[Index(35)]
public string col036 { get; set; }
[Index(36)]
public string col037 { get; set; }
[Index(37)]
public string col038 { get; set; }
[Index(38)]
public string col039 { get; set; }
[Index(39)]
public string col040 { get; set; }
[Index(40)]
public string col041 { get; set; }
[Index(41)]
public string col042 { get; set; }
[Index(42)]
public string col043 { get; set; }
[Index(43)]
public string col044 { get; set; }
[Index(44)]
public string col045 { get; set; }
[Index(45)]
public string col046 { get; set; }
[Index(46)]
public string col047 { get; set; }
[Index(47)]
public string col048 { get; set; }
[Index(48)]
public string col049 { get; set; }
[Index(49)]
public string col050 { get; set; }
[Index(50)]
public string col051 { get; set; }
[Index(51)]
public string col052 { get; set; }
[Index(52)]
public string col053 { get; set; }
[Index(53)]
public string col054 { get; set; }
[Index(54)]
public string col055 { get; set; }
[Index(55)]
public string col056 { get; set; }
[Index(56)]
public string col057 { get; set; }
[Index(57)]
public string col058 { get; set; }
[Index(58)]
public string col059 { get; set; }
[Index(59)]
public string col060 { get; set; }
[Index(60)]
public string col061 { get; set; }
[Index(61)]
public string col062 { get; set; }
[Index(62)]
public string col063 { get; set; }
[Index(63)]
public string col064 { get; set; }
[Index(64)]
public string col065 { get; set; }
[Index(65)]
public string col066 { get; set; }
[Index(66)]
public string col067 { get; set; }
[Index(67)]
public string col068 { get; set; }
[Index(68)]
public string col069 { get; set; }
[Index(69)]
public string col070 { get; set; }
[Index(70)]
public string col071 { get; set; }
[Index(71)]
public string col072 { get; set; }
[Index(72)]
public string col073 { get; set; }
[Index(73)]
public string col074 { get; set; }
[Index(74)]
public string col075 { get; set; }
[Index(75)]
public string col076 { get; set; }
[Index(76)]
public string col077 { get; set; }
[Index(77)]
public string col078 { get; set; }
[Index(78)]
public string col079 { get; set; }
[Index(79)]
public string col080 { get; set; }
[Index(80)]
public string col081 { get; set; }
[Index(81)]
public string col082 { get; set; }
[Index(82)]
public string col083 { get; set; }
[Index(83)]
public string col084 { get; set; }
[Index(84)]
public string col085 { get; set; }
[Index(85)]
public string col086 { get; set; }
[Index(86)]
public string col087 { get; set; }
[Index(87)]
public string col088 { get; set; }
[Index(88)]
public string col089 { get; set; }
[Index(89)]
public string col090 { get; set; }
[Index(90)]
public string col091 { get; set; }
[Index(91)]
public string col092 { get; set; }
[Index(92)]
public string col093 { get; set; }
[Index(93)]
public string col094 { get; set; }
[Index(94)]
public string col095 { get; set; }
[Index(95)]
public string col096 { get; set; }
[Index(96)]
public string col097 { get; set; }
[Index(97)]
public string col098 { get; set; }
[Index(98)]
public string col099 { get; set; }
[Index(99)]
public string col100 { get; set; }
}
}
で、30分で作業は可能なの?
試してみました。
プロジェクトの作成(1分)
パッケージのインストールとコードのコピペ(2分)
指定の項目の入れ替えや加工(10分)
検証(5分)
合計18分でした!