質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio 2013

Microsoft Visual Studio 2013は、Microsoftによる統合開発環境(IDE)であり、多種多様なプログラミング言語に対応しています。 Visual Studio 2012の次のバージョンです

Q&A

解決済

2回答

9414閲覧

C#でCSVを生成する際、ユーザがダイアログで指定した場所にうまく保存できません。

toshi0607

総合スコア56

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio 2013

Microsoft Visual Studio 2013は、Microsoftによる統合開発環境(IDE)であり、多種多様なプログラミング言語に対応しています。 Visual Studio 2012の次のバージョンです

0グッド

0クリップ

投稿2015/09/30 16:54

編集2015/10/06 15:27

概要

下記のようなBuilderを記述の上、ユーザが指定した場所にCSVファイルを保存したいです。下記のようにすると、

C:\Users\[ユーザ名]\Documents\Visual Studio 2013\Projects\[プロジェクト名]\[アプリ名]\bin\Debug

下には保存されるのですが、デスクトップ等ユーザが選択した場所に保存できません。

lang

1class CsvBuilder : IDisposable 2 { 3 /// <summary> 4 /// CSVファイルに書き込むストリーム 5 /// </summary> 6 private StreamWriter stream = null; 7 8 /// <summary> 9 /// ファイル名を指定して、 <see cref="CsvWriter">CsvWriter</see> クラスの新しいインスタンスを初期化します。 10 /// </summary> 11 /// <param name="path">書き込む完全なファイルパス。</param> 12 public CsvBuilder(string path) : 13 this(path, Encoding.Default) 14 { 15 } 16 17 /// <summary> 18 /// ファイル名、文字エンコーディングを指定して、 <see cref="CsvWriter">CsvWriter</see> クラスの新しいインスタンスを初期化します。 19 /// </summary> 20 /// <param name="path">書き込む完全なファイルパス。</param> 21 /// <param name="encoding">使用する文字エンコーディング。</param> 22 public CsvBuilder(string path, Encoding encoding) 23 { 24 var stream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite); 25 this.stream = new StreamWriter(stream, encoding); 26 } 27 28 /// <summary> 29 /// 使用する文字エンコーディングを取得します。 30 /// </summary> 31 public Encoding Encoding 32 { 33 get 34 { 35 return this.stream.Encoding; 36 } 37 } 38 39 /// <summary> 40 /// 現在のストリームで利用される改行文字列を取得または設定します。 41 /// </summary> 42 public string NewLine 43 { 44 get 45 { 46 return this.stream.NewLine; 47 } 48 49 set 50 { 51 this.stream.NewLine = value; 52 } 53 } 54 55 /// <summary> 56 /// 現在のストリームオブジェクトと基になるストリームをとじます。 57 /// </summary> 58 public void Close() 59 { 60 if (this.stream == null) 61 { 62 return; 63 } 64 65 this.stream.Close(); 66 } 67 68 /// <summary> 69 /// CsvWriter で利用されているすべてのリソースを解放します。 70 /// </summary> 71 public void Dispose() 72 { 73 if (this.stream == null) 74 { 75 return; 76 } 77 78 this.stream.Close(); 79 this.stream.Dispose(); 80 this.stream = null; 81 } 82 83 /// <summary> 84 /// 現在のライターで使用したすべてのバッファーをクリアし、バッファー内のすべてのデータをストリームに書き込みます。 85 /// </summary> 86 public void Flush() 87 { 88 this.stream.Flush(); 89 } 90 91 /// <summary> 92 /// 現在のライターで使用したすべてのバッファーを非同期的にクリアし、ストリームへ書き込みます。 93 /// </summary> 94 /// <returns>非同期のフラッシュ操作を表すタスク。</returns> 95 public Task FlushAsync() 96 { 97 return this.stream.FlushAsync(); 98 } 99 100 /// <summary> 101 /// ストリームに文字を書き込みます。 102 /// </summary> 103 /// <typeparam name="T">リストの型。</typeparam> 104 /// <param name="data">CSVデータ。</param> 105 public void Write<T>(List<List<T>> data, string bankName, string extensiton) 106 { 107 SaveFileDialog sfd = new SaveFileDialog(); 108 sfd.FileName = CreateDateTimeFileName(name, extension); 109 sfd.InitialDirectory = @"C:\"; 110 sfd.Title = "ファイルの保存先を指定してください。"; 111 sfd.Filter = "csv files (*.csv)|*.csv"; 112 113 if (sfd.ShowDialog() == DialogResult.OK) 114 { 115 foreach (var row in data) 116 { 117 this.WriteRow<T>(row); 118 } 119 } 120 } 121 122 /// <summary> 123 /// ストリームに1レコード分の文字列を書き込みます。 124 /// </summary> 125 /// <typeparam name="T">リストの型。</typeparam> 126 /// <param name="row">CSVの1レコード。</param> 127 public void WriteRow<T>(List<T> row) 128 { 129 var sb = new StringBuilder(); 130 131 foreach(var cell in row) 132 { 133 var value = cell.ToString(); 134 135 if (value.Contains(this.NewLine) || 136 value.Contains(",") || 137 value.Contains("\"")) 138 { 139 value = value.Replace("\"", "\"\""); 140 sb.Append("\""); 141 sb.Append(value); 142 sb.Append("\""); 143 } 144 else 145 { 146 sb.Append(value); 147 } 148 149 sb.Append(","); 150 } 151 152 sb.Remove(sb.Length - 1, 1); 153 154 this.stream.WriteLine(sb.ToString()); 155 } 156 157 /// <summary> 158 /// ストリームに1レコード分の文字列を非同期的に書き込みます。 159 /// </summary> 160 /// <typeparam name="T">リストの型。</typeparam> 161 /// <param name="row">CSVの1レコード。</param> 162 /// <returns>非同期の書き込み操作を表すタスク。</returns> 163 public Task WriteRowAsync<T>(List<T> row) 164 { 165 return Task.Factory.StartNew(() => 166 { 167 this.WriteRow<T>(row); 168 }); 169 } 170 171 /// <summary> 172 /// 日付名のファイル名を返す 173 /// </summary> 174 /// <param name="extension">拡張子</param> 175 /// <returns>拡張子を含んだ、日付のファイル名</returns> 176 public static string CreateDateTimeFileName(string bankName, string extension) 177 { 178 return DateTime.Now.ToString("yyyyMMddhhmmss") + bankName + extension; 179 } 180 181 }

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

lang

1using (var csvBuilder = new CsvBuilder(CsvBuilder.CreateDateTimeFileName("Name", ".csv"))) 2 { 3 csvBuilder.Write(list, "Name", ".csv"); 4 }

動作環境

・.NET Frame Work 4.5
・Visual Studio2013

追加で…

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

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

CsvBuilderクラスのコンストラクタを見ると、第一引数には「書き込む完全なファイルパス」を与えることになっていますね?
一方、「CsvBuilder.CreateDateTimeFileName("SMBC_biz", ".csv")」って、タイムスタンプを付加した「ファイル名」しか返さないのではないでしょうか?

ですから、この実装方法だと、FileStreamNew した時点では「path」にはファイル名しか代入されていないので、「カレントディレクトリ」にしかファイルが出力されないのだと思います。

argiusさんがご指摘のように、「SaveFileDialog」を使用して出力先ファイルをユーザーが選択できるようにしたいなら、SaveFileDialog は FileStream を New するに呼び出して、選択したファイルが出力先としてオープンされるようにする必要があります。

ただし、FileNameプロパティではファイル名しか取得できないので、結局はカレントディレクトリへの出力となってしまいます。

それで、MSDNのページに載っている実装例のように
var stream = sfd.OpenFile()
として、実際に選択されたファイルをオープンするのが正しいと思います。

それから、

Debugフォルダ下に作成されるCSVファイルは削除する処理を入れるべきものでしょうか?

についてですが、テスト環境のお掃除処理(=本番環境では不要)をプログラム内に実装するのはおかしいと思います。

通常は__「テスト環境維持のための日次バッチ」__のような感じでお掃除を実行しているケースが多いのではないでしょうか?
具体的には、Debugディレクトリ配下のCSVファイルを削除するコマンドを、crontabに設定するなどして1回/日 実行すれば良いのではないでしょうか?

投稿2015/09/30 20:41

pi-chan

総合スコア5936

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

toshi0607

2015/10/06 15:29

ありがとうございます!ユーザが保存場所選択後、FileNameプロパティでフルパスが取得できたのでそれを踏まえてStream処理するようにしました。 お掃除処理に関する知見もありがとうございます!!
guest

0

ベストアンサー

ファイル保存ダイアログから、選択されたファイル名を取得してからファイルオープンしないといけないのではないでしょうか。

sfd.FileNameプロパティーでファイル名を取得し、その後でFileStreamnewする必要があると思います。

参考リンク:
SaveFileDialog クラス (System.Windows.Forms)

投稿2015/09/30 17:09

編集2015/09/30 23:14
argius

総合スコア9388

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pi-chan

2015/09/30 19:24

argiusさん、せっかく貼り付けてくださった参考リンクですが、MSDNのURLって、URL自体にカッコ "(", ")" を含んでいるので、URL/リンクを表すマークダウン記法と相性が悪いです。(クリックしても目的のページへジャンプできません。) お手数ですが、URLを「文字列」として貼り付けて頂けませんか?
argius

2015/09/30 23:14

pi-chanさん ご指摘ありがとうございます。すみません、ちゃんと確認してませんでした... リンクを修正しました。
toshi0607

2015/10/06 15:24

下記のように修正することで適正に処理できました!ありがとうございました。 ```lang-C# SaveFileDialog sfd = new SaveFileDialog(); sfd.FileName = CsvBuilder.CreateDateTimeFileName("Name", ".csv"); sfd.InitialDirectory = @"C:\"; sfd.Title = "明細ファイルの保存先を指定してください。"; sfd.Filter = "csv files (*.csv)|*.csv"; if (sfd.ShowDialog() == DialogResult.OK) { using (var csvBuilder = new CsvBuilder(sfd.FileName)) { csvBuilder.Write(wtxns); } } ```
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問