using KssSmaPlaLib.Commons; using KssSmaPlaLib.Exchange.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace KssSmaPlaLib.IO.File { public class CsvUxer { public static bool ExportToCsv(List list, string filePath, out string resultMessage, EncodingUxer.EncodingType encodingType = EncodingUxer.EncodingType.Utf8WithoutBom) where T : ICsvExportableUxer { resultMessage = string.Empty; try { var lines = new List { list.FirstOrDefault()?.CsvHeader ?? "" }; lines.AddRange(list.Select(item => item.ToCsvLine())); System.IO.File.WriteAllLines(filePath, lines, EncodingUxer.GetEncoding(encodingType)); return true; } catch (Exception ex) { resultMessage = $"CSV出力に失敗しました。[ファイルパス:{filePath},エラー内容:{ex.Message}]"; return false; } } /// /// CSVインポート /// /// CSVファイルパス /// [出力]レコードセット /// [出力]メッセージ /// [オプション]エンコード /// 成否 public static bool ImportFromCsv(string csvFilePath, out List> recordSet, out string resultMessage, EncodingUxer.EncodingType encodingType = EncodingUxer.EncodingType.Utf8WithoutBom) { recordSet = new List>(); resultMessage = string.Empty; try { // 全行読み込み var lines = System.IO.File.ReadAllLines(csvFilePath, EncodingUxer.GetEncoding(encodingType)); // データ行0件 if (lines.Length < 2) return true; // ヘッダ行項目分割 var header = Regex.Split(lines[0], ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"); for (var cnt = 0; cnt < header.Length; cnt++) header[cnt] = header[cnt].Trim('\"'); // データ行件数分 for (int i = 1; i < lines.Length; i++) { // 項目分割 var values = Regex.Split(lines[i], ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)"); for (var cnt = 0; cnt < values.Length; cnt++) values[cnt] = values[cnt].Trim('\"'); if (values.Length != header.Length) { resultMessage = $"CSV読み込みでヘッダの項目数とデータの項目数が異なります。[CSVファイルパス:{csvFilePath},行番号:{i + 1},ヘッダ行項目数:{header.Length},データ行項目数:{values.Length}]"; return false; } var record = new Dictionary(); // 全項目 for (int j = 0; j < header.Length && j < values.Length; j++) { var key = header[j].Trim('\"'); var value = values[j].Trim('\"'); record[key] = value; } recordSet.Add(record); } return true; } catch (Exception ex) { resultMessage = $"CSV読み込み処理でエラーが発生しました。[CSVファイルパス:{csvFilePath},エラー内容:{ex.Message}]"; return false; } } public static string Escape(string value) => $"\"{value.Replace("\"", "\"\"")}\""; public static string Join(params object[] values) => string.Join(",", values.Select(m => Escape(m == null ? "" : m.ToString()))); /// /// CSVファイルを読み込み、ディクショナリに変換します。 /// /// CSVファイルのパス /// 出力されるディクショナリ(ファイル無しの場合は空) /// /// 処理が成功した場合はtrue、例外発生時はfalse public static bool TryGetDictionaryFromCsv( string path, out Dictionary dic, out string resultMessage) { resultMessage = string.Empty; // 1. 最初に出力変数を空のディクショナリで初期化 dic = new Dictionary(); try { // 2. ファイルが存在しない場合は空のままtrueで返す if (!System.IO.File.Exists(path)) { return true; } // 3. BOM付きUTF-8を指定してファイルを開く // (Encoding.UTF8 はデフォルトでBOMの有無を自動判別して正しく読み込めます) using (var reader = new StreamReader(path, Encoding.UTF8)) { while (!reader.EndOfStream) { string line = reader.ReadLine(); // 空行はスキップ if (string.IsNullOrWhiteSpace(line)) { continue; } // カンマで分割 (単純な2列前提) string[] parts = line.Split(','); // 最低1列(キー)があれば処理する(2列未満の対策) if (parts.Length > 0) { string key = parts[0]; // 2列目があればその値、なければ空文字 string value = parts.Length > 1 ? parts[1] : string.Empty; // 重複キーは後勝ち(インデクサを使うことで上書き保存) dic[key] = value; } } } return true; } catch (Exception ex) { // TODO: あたしがのちほどエラーメッセージ処理を入れる! resultMessage = $"CSVファイル読み込みに失敗しました。[詳細:{ex.Message}]"; return false; } } } }