diff --git a/TIASshot/Utils/IoUtil.cs b/TIASshot/Utils/IoUtil.cs index 95f9d16..a731ece 100644 --- a/TIASshot/Utils/IoUtil.cs +++ b/TIASshot/Utils/IoUtil.cs @@ -131,18 +131,33 @@ } /// - /// 設定値をCSVファイルに書き込む + /// 設定値をCSVファイルに書き込む. + /// 値(ユーザー入力のデータ名・保存フォルダのパス等)にカンマ・ダブルクォート・改行が + /// 含まれても行が崩れないよう,RFC 4180 に従ってクォートする(該当しない値は従来どおり). /// /// /// public static void WriteInfo(StreamWriter sw, Dictionary info) { sw.WriteLine($"撮影日時,{DateTime.Now:yyyy/MM/dd HH:mm:ss}"); // 撮影日時を追加 foreach (var kvp in info) { - sw.WriteLine($"{kvp.Key},{kvp.Value}"); + sw.WriteLine($"{EscapeCsvField(kvp.Key)},{EscapeCsvField(kvp.Value)}"); } } /// + /// CSV の 1 フィールドを RFC 4180 に従ってエスケープする. + /// カンマ・ダブルクォート・CR・LF を含む場合のみ全体をダブルクォートで囲み, + /// 内部のダブルクォートを二重化する.それ以外はそのまま返す(null は空文字). + /// + /// フィールド値(null 可) + /// エスケープ済みのフィールド + private static string EscapeCsvField(string field) { + if (string.IsNullOrEmpty(field)) return ""; + if (field.IndexOfAny(new[] { ',', '"', '\r', '\n' }) < 0) return field; + return "\"" + field.Replace("\"", "\"\"") + "\""; + } + + /// /// 効果音を再生する /// ///