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

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

ただいまの
回答率

90.61%

  • C#

    6847questions

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

  • CSV

    602questions

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

  • Visual Studio 2013

    306questions

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

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,336

toshi0607

score 49

 概要

下記のような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>
        /// ストリームに1レコード分の文字列を書き込みます。
        /// </summary>
        /// <typeparam name="T">リストの型。</typeparam>
        /// <param name="row">CSVの1レコード。</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>
        /// ストリームに1レコード分の文字列を非同期的に書き込みます。
        /// </summary>
        /// <typeparam name="T">リストの型。</typeparam>
        /// <param name="row">CSVの1レコード。</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ファイルは削除する処理を入れるべきものでしょうか?開発時の確認にはあった方が便利ですが、延々とたまっていくのをたまにまとめて削除という運用も微妙かと思っています。


よろしくお願いします。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

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

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

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

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

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

それから、
Debugフォルダ下に作成されるCSVファイルは削除する処理を入れるべきものでしょうか?
についてですが、テスト環境のお掃除処理(=本番環境では不要)をプログラム内に実装するのはおかしいと思います。

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/07 00:29

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

    お掃除処理に関する知見もありがとうございます!!

    キャンセル

checkベストアンサー

+1

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

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


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/01 04:24

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

    キャンセル

  • 2015/10/01 08:14

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

    キャンセル

  • 2015/10/07 00: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);
    }
    }
    ```

    キャンセル

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

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    C#でlogを出力する方法(log4net無しで)

    デバッグとして変数をlogに出力したいのですが検索するとlog4netを使用する例が多く出てきます。 チーム制作中なのでdllを追加する等はあまりやりたくないです、、、 .txt形

  • 受付中

    unity の外部参照について

    unityを使ってエディターを作成しています。使用言語はc#です。 OSはWindowsとMacを対象(開発はMac) unityeditor上で読み込む(ダイアログボックスを出す

  • 解決済

    IE8でファイルダウンロードが出来ない

    質問   現在、ASP.NETにてWEBアプリケーションを開発しています。    そこでファイルをダウンロードする機能を作成しているのですが、IE8でファイルのダウンロードが失敗

  • 受付中

    pythonでmysqlconnectorを使う際

    pythonでmysqlを操作して、日本語を取り出しその後それをcsvに書き込みたいのですが、 res = cursor.fetchall() で取り出した後に、どこかで .

  • 解決済

    C# CoincheckのAPIを実行したい。

    お世話になります。 C#の勉強をしたいと思い 下記のサイトより https://kokenji.net/coincheck-api/ コードを拝借してAPIを実行してみまし

  • 解決済

    VB.NETでデータを保持したい

    Visual Studioを使いVB.NETでウィンドウズアプリケーションを作成しています。 DataGridViewを配置し、CSVを開きそのデータをDataGridView

  • 解決済

    C# HTMLタグを除去したい

    実現したいこと HTMLタグ(<!--~-->の中身/</a>・</span>・</b>)などの除去の仕方がわかりません。どう処理したらいいのでしょうか。取得はHTML Agili

  • 解決済

    C#+SeleniumでStaleElementReferenceError

    C#でSeleniumを使用して自動化処理を書いています。 (ブラウザはChromeです。) OS: Windows10 IDE: Visual Studio 2015 Sel

同じタグがついた質問を見る

  • C#

    6847questions

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

  • CSV

    602questions

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

  • Visual Studio 2013

    306questions

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

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