17123
# 概要

下記のようなBuilderを記述の上、ユーザが指定した場所にCSVファイルを保存したいです。下記のようにすると、`C:\Users\[ユーザ名]\Documents\Visual
Studio
2013\Projects\[プロジェクト名]\[アプリ名]\bin\Debug`下には保存されるのですが、デスクトップ等ユーザが選択した場所に保存できません。

```

    class CsvBuilder : IDisposable
    {
        /// <summary>
        /// CSVファイルに書き込むストリーム
        /// </summary>
        private StreamWriter stream = null;
    
        /// <summary>
        /// ファイル名を指定して、 <see cref="CsvWriter">CsvWriter</see> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="path">書き込む完全なファイルパス。</param>
        public CsvBuilder(string path) :
            this(path, Encoding.Default)
        {
        }
    
        /// <summary>
        /// ファイル名、文字エンコーディングを指定して、 <see cref="CsvWriter">CsvWriter</see> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="path">書き込む完全なファイルパス。</param>
        /// <param name="encoding">使用する文字エンコーディング。</param>
        public CsvBuilder(string path, Encoding encoding)
        {
            var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
            this.stream = new StreamWriter(stream, encoding);
        }
    
        /// <summary>
        /// 使用する文字エンコーディングを取得します。
        /// </summary>
        public Encoding Encoding
        {
            get
            {
                return this.stream.Encoding;
            }
        }
    
        /// <summary>
        /// 現在のストリームで利用される改行文字列を取得または設定します。
        /// </summary>
        public string NewLine
        {
            get
            {
                return this.stream.NewLine;
            }
    
            set
            {
                this.stream.NewLine = value;
            }
        }
    
        /// <summary>
        /// 現在のストリームオブジェクトと基になるストリームをとじます。
        /// </summary>
        public void Close()
        {
            if (this.stream == null)
            {
                return;
            }
    
            this.stream.Close();
        }
    
        /// <summary>
        /// CsvWriter で利用されているすべてのリソースを解放します。
        /// </summary>
        public void Dispose()
        {
            if (this.stream == null)
            {
                return;
            }
    
            this.stream.Close();
            this.stream.Dispose();
            this.stream = null;
        }
    
        /// <summary>
        /// 現在のライターで使用したすべてのバッファーをクリアし、バッファー内のすべてのデータをストリームに書き込みます。
        /// </summary>
        public void Flush()
        {
            this.stream.Flush();
        }
    
        /// <summary>
        /// 現在のライターで使用したすべてのバッファーを非同期的にクリアし、ストリームへ書き込みます。
        /// </summary>
        /// <returns>非同期のフラッシュ操作を表すタスク。</returns>
        public Task FlushAsync()
        {
            return this.stream.FlushAsync();
        }
    
        /// <summary>
        /// ストリームに文字を書き込みます。
        /// </summary>
        /// <typeparam name="T">リストの型。</typeparam>
        /// <param name="data">CSVデータ。</param>
        public void Write<T>(List<List<T>> data, string bankName, string extensiton)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.FileName = CreateDateTimeFileName(name, extension);
            sfd.InitialDirectory = @"C:\";
            sfd.Title = "ファイルの保存先を指定してください。";
            sfd.Filter = "csv files (*.csv)|*.csv";
    
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                foreach (var row in data)
                {
                    this.WriteRow<T>(row);
                }
            }
        }
    
        /// <summary>
        /// ストリームに１レコード分の文字列を書き込みます。
        /// </summary>
        /// <typeparam name="T">リストの型。</typeparam>
        /// <param name="row">CSVの１レコード。</param>
        public void WriteRow<T>(List<T> row)
        {
            var sb = new StringBuilder();
    
            foreach(var cell in row)
            {
                var value = cell.ToString();
    
                if (value.Contains(this.NewLine) ||
                    value.Contains(",") ||
                    value.Contains("\""))
                {
                    value = value.Replace("\"", "\"\"");
                    sb.Append("\"");
                    sb.Append(value);
                    sb.Append("\"");
                }
                else
                {
                    sb.Append(value);
                }
    
                sb.Append(",");
            }
    
            sb.Remove(sb.Length - 1, 1);
    
            this.stream.WriteLine(sb.ToString());
        }
    
        /// <summary>
        /// ストリームに１レコード分の文字列を非同期的に書き込みます。
        /// </summary>
        /// <typeparam name="T">リストの型。</typeparam>
        /// <param name="row">CSVの１レコード。</param>
        /// <returns>非同期の書き込み操作を表すタスク。</returns>
        public Task WriteRowAsync<T>(List<T> row)
        {
            return Task.Factory.StartNew(() =>
            {
                this.WriteRow<T>(row);
            });
        }
    
        /// <summary>
        /// 日付名のファイル名を返す
        /// </summary>
        /// <param name="extension">拡張子</param>
        /// <returns>拡張子を含んだ、日付のファイル名</returns>
        public static string CreateDateTimeFileName(string bankName, string extension)
        {
            return DateTime.Now.ToString("yyyyMMddhhmmss") + bankName + extension;
        }
    
    }
    
```

・ 使用する側のコードは下記のようなものです。

```

    using (var csvBuilder = new CsvBuilder(CsvBuilder.CreateDateTimeFileName("Name", ".csv")))
    {
        csvBuilder.Write(list, "Name", ".csv");
    }
    
```

# 動作環境

・.NET Frame Work 4.5  
・Visual Studio2013

# 追加で…

Debugフォルダ下に作成されるCSVファイルは削除する処理を入れるべきものでしょうか？開発時の確認にはあった方が便利ですが、延々とたまっていくのをたまにまとめて削除という運用も微妙かと思っています。

よろしくお願いします。
CC BY-SA 3.0
2015-09-30T16:49:43.160
17122
2015-10-06T15:30:59.840
2015-10-06T15:30:59.840
12459
12459
question
c# visual-studio
C#でCSVを生成する際、ユーザがダイアログで指定した場所にうまく保存できません。
`CsvBuilder`のインスタンス生成時に指定している`CsvBuilder.CreateDateTimeFileName("SMBC_biz",
".csv")`はファイル名のみを返しており、この値で作成された`StreamWriter`を`CsvBuilder.Write`で保存先が指定した後も使用し続けているためです。なので同メソッドの`foreach`の前に

```

    this.stream.Close(); // ※Close()やDispose()はどちらかを1回だけ実行する
    
    var stream = new FileStream(sfd.FileName, FileMode.Create, FileAccess.ReadWrite);
    this.stream = new StreamWriter(stream, encoding);
    
```

を追加すれば`SaveFileDialog`の選択ファイルを使用するようになります。

しかし上記のように修正するより、`Write`の処理をを`CsvBuilder`のインスタンスメソッド以外のどこかに移動する方がきれいな設計になるかと思います。

```

    SaveFileDialog sfd = new SaveFileDialog();
    sfd.FileName = CsvBuilder.CreateDateTimeFileName(name, extension); // 変更
    sfd.InitialDirectory = @"C:\";
    sfd.Title = "ファイルの保存先を指定してください。";
    sfd.Filter = "csv files (*.csv)|*.csv";
    
    if (sfd.ShowDialog() == DialogResult.OK)
    {
        using(var csvBuilder = new CsvBuilder(sfd.FileName)) // 追加
        {
            foreach (var row in data)
            {
                csvBuilder.WriteRow<T>(row); // 変更
            }
        }
    }
    
```
CC BY-SA 3.0
2015-09-30T21:20:23.817
17123
2015-10-01T05:48:39.933
2015-10-01T05:48:39.933
5750
5750
17122
answer