none
カンマ交じりの文のあるデータをCSVにする RRS feed

  • 質問

  • カンマ区切りのCSVファイルを扱いたいと思っています。
    読み書きするにあたって、もともと(',')のないデータを扱うのは問題ないのですが、
    カンマがある文交じりのデータの場合の適切な処理方法が分かりません。
    どうすべきでしょうか?
    例えば、";;;"の文字列を入力不能としたうえで、","を必ず";;;"に置き換えるなどの方法をとっているのでしょうか?

    このあたりを、教えて頂けませんでしょうか?
    よろしくお願い致します。

    2013年7月14日 2:42

回答

  • http://ja.wikipedia.org/wiki/Comma-Separated_Values

    // 参照元のRFC4180自体、そのまま従うと日本語を記述できない残念仕様ですが……。

    • 編集済み Hongliang 2013年7月14日 3:23 リンク追加
    • 回答の候補に設定 Jitta 2013年7月15日 11:37
    • 回答としてマーク AzuleanMVP, Moderator 2013年7月15日 22:14
    2013年7月14日 3:22
  • 改行コードやデリミタ(コンマのような区切り文字)がフィールド値の中にあったり、改行コードが異なったり(例: Windows は CR + LF、Unix は LF)するのに対応する場合、フィールド値をダブルクォート (") で囲むと言った約束を設けて対応します。

    が、そのような CSV ファイルの処置が可能なパーサーを自力で作るのは簡単ではありませんので、既存のパーサーを使うのがよさそうです。

    http://surferonwww.info/BlogEngine/post/2010/10/28/CSV-parser.aspx
    CSV パーサー

    2013年7月14日 5:33
  •  エクセルにカンマ混じりの入力を行って、CSV 出力させ、どのように出力しているか見れば良いのでは?

     とはいえ、すでに出ているように、ダブルクォーテーションで囲んでいますけど。
     そうすると今度は、「ダブルクォーテーションは、どのように出すの?」と疑問が出てくれば正解。それも入力して、確認すれば良い。


    Jitta@<a href="http://www.wankuma.com">わんくま同盟</a>

    • 回答としてマーク FacePanel 2013年7月15日 0:41
    2013年7月14日 12:58

すべての返信

  • http://ja.wikipedia.org/wiki/Comma-Separated_Values

    // 参照元のRFC4180自体、そのまま従うと日本語を記述できない残念仕様ですが……。

    • 編集済み Hongliang 2013年7月14日 3:23 リンク追加
    • 回答の候補に設定 Jitta 2013年7月15日 11:37
    • 回答としてマーク AzuleanMVP, Moderator 2013年7月15日 22:14
    2013年7月14日 3:22
  • 改行コードやデリミタ(コンマのような区切り文字)がフィールド値の中にあったり、改行コードが異なったり(例: Windows は CR + LF、Unix は LF)するのに対応する場合、フィールド値をダブルクォート (") で囲むと言った約束を設けて対応します。

    が、そのような CSV ファイルの処置が可能なパーサーを自力で作るのは簡単ではありませんので、既存のパーサーを使うのがよさそうです。

    http://surferonwww.info/BlogEngine/post/2010/10/28/CSV-parser.aspx
    CSV パーサー

    2013年7月14日 5:33
  • おかしなことになっていました。

    分からないことが2つあります。
    一つは、読み込む方ではなく、書き込みのときに、「,」を「","」に変える作業が必要なのかと解釈したのですが、それで間違いないのでしょうか。
    二つ目は、もしもそうだとすると、「,」を「","」に変えたいのですが、C#でReplaceを使うと
    Replace(',', "','")となって、Replaceの引数が3つになってしまうので、1行のコード自体がエラーになってしまいます。

    不勉強で申し訳ありませんが、ご解説頂けませんでしょうか。

    2013年7月14日 6:41
  • ご自分の書きたいことだけを一方的に書くのではなく、レスをもらったらまずそれに解答するなどされてはいかがですか。

    フォーラムなのですから、お互い役に立つ情報をやり取りして、課題解決に向け進めて行くという方向で考えていただければと思います。

    2013年7月14日 6:53
  • 申し訳ありません、パーサーについて、非常に難しいことが書かれておりましたので、頭の中がはちきれてしまいました。
    そのため、お礼を忘れておりました。お詫び申し上げます。

    お答えいただきました内容をみますと、特殊な文字があるときにその文字を変換して読み取っていることが分かります。

    2013年7月14日 7:15
  • > お礼を忘れておりました。

    お礼は結構ですから、先のレスをどこまで理解したか、不明な点があれば何かを書いて下さい。そのようにして、お互い理解しあいながら進めていかないと、掲示板ではなかなか話が通じません。

    最初の質問に対しては、先の自分のレス「改行コードやデリミタ(コンマのような区切り文字)がフィールド値の中にあったり、改行コードが異なったり(例: Windows は CR + LF、Unix は LF)するのに対応する場合、フィールド値をダブルクォート (") で囲むと言った約束を設けて対応します。」が十分回答になっていると思っています。

    これが回答になっていない、もしくは内容が分からないということなら、具体的に何が不足か書いてもらわないと話が進められません。

    いきなり別の質問に飛んでますが、そういうのは混乱を招くだけと思います。

    2013年7月14日 7:35
  •  エクセルにカンマ混じりの入力を行って、CSV 出力させ、どのように出力しているか見れば良いのでは?

     とはいえ、すでに出ているように、ダブルクォーテーションで囲んでいますけど。
     そうすると今度は、「ダブルクォーテーションは、どのように出すの?」と疑問が出てくれば正解。それも入力して、確認すれば良い。


    Jitta@<a href="http://www.wankuma.com">わんくま同盟</a>

    • 回答としてマーク FacePanel 2013年7月15日 0:41
    2013年7月14日 12:58
  • 正確な資料のご提示ありがとうございました。

    よく読んでみると、分かりやすい資料でした。

    2013年7月15日 0:40
  • 具体的な勉強方法をいただきまして、ありがとうございます。
    1日寝て睡眠不足を解消した頭となったせいか、すっきり理解できます。

    で、ここは公開討論会の場だったのですね。Forumの意味が分かっていなくて変な質問となってしまい、申し訳ありませんでした。(いい訳です:)

    2013年7月15日 0:45
  • 理屈はある程度理解されたと思います。実装はTextFieldParser クラスを使うと便利です。

    何故か Microsoft.VisualBasic.dll 内 にありますが、もちろん Visual C# Express Edition からも使えます。

    ITmediaのTIPSにある以下の記事が解りやすいと思います。

    CSVファイルを読み込むには?[2.0のみ、C#、VB]


    • 編集済み hihijiji 2013年7月15日 2:18 日本語修正
    2013年7月15日 1:28
  • > ここは公開討論会の場だったのですね。

    自分としては、MSDN フォーラムは、開発者同士の情報交換の場所と理解しています。なので、質問者と回答者だけ Q&A だけではなく、検索などでここを訪れた人の役にも立てればよりよいと思っています。

    ただし、今回のやり取りは Q&A 目的だけでも問題ありと思いませんか? 例えば:

    (1) いきなり「おかしなことになっていました。」とレスされましたが回答者には意味不明です。何がおかしいのですか?

    (2) 「書き込みのときに、「,」を「","」に変える作業が必要」というのは、フィールドをダブルクォートで囲む手法として質問者さんが考えたことらしいと想像してますが、そうであれば「フィールドをダブルクォートで囲むことで対応するのは理解した。その手法として CSV ファイル作成時に「,」を「","」に変える作業が必要と解釈したが、それで間違いないか?」というように聞いてもらったほうが良いと思いませんか? (そうしてもらえたら「そうではありません。XXXのようにで行います・・・」と答えられたのですが。)

    (3) 複数回答がありますが、誰の回答に対するレスなのかよく分からない。

    (4) 結局、最終的に、どこまで理解していただいたのか分からない。


    • 編集済み SurferOnWww 2013年7月15日 1:42 誤字訂正
    2013年7月15日 1:41
  • その後、Webを検索しまくって、寝ずに考えて

        class CsvFile
        {
            public string SaveInCsv(System.Data.DataTable dt, string csvPath)
            {
                System.Text.Encoding enc = System.Text.Encoding.GetEncoding("Shift_JIS");

                try
                {
                    System.IO.StreamWriter sw = new System.IO.StreamWriter(csvPath, false, enc);

                    int clmCount = dt.Columns.Count;
                    int lastClmIndex = clmCount - 1;

                    // ヘッダー
                    for (int i = 0; i < clmCount; i++)
                    {
                        string field = dt.Columns[i].Caption;
                        if (field.IndexOf('"') > -1 ||
                            field.IndexOf(',') > -1 ||
                            field.IndexOf('\r') > -1 ||
                            field.IndexOf('\n') > -1 ||
                            field.StartsWith(" ") ||
                            field.StartsWith("\t") ||
                            field.EndsWith(" ") ||
                            field.EndsWith("\t"))
                        {
                            if (field.IndexOf('"') > -1)
                                field = field.Replace("\"", "\"\"");
                            field = "\"" + field + "\"";
                        }
                        sw.Write(field);
                        if (lastClmIndex > i)
                            sw.Write(',');
                    }
                    sw.Write("\r\n");

                    foreach (System.Data.DataRow row in dt.Rows)
                    {
                        for (int i = 0; i < clmCount; i++)
                        {
                            string field = row[i].ToString();
                            if (field.IndexOf('"') > -1 ||
                                field.IndexOf(',') > -1 ||
                                field.IndexOf('\r') > -1 ||
                                field.IndexOf('\n') > -1 ||
                                field.StartsWith(" ") ||
                                field.StartsWith("\t") ||
                                field.EndsWith(" ") ||
                                field.EndsWith("\t"))
                            {
                                if (field.IndexOf('"') > -1)
                                    field = field.Replace("\"", "\"\"");
                                field = "\"" + field + "\"";
                            }
                            sw.Write(field);
                            if (lastClmIndex > i)
                                sw.Write(',');
                        }
                        sw.Write("\r\n");
                    }
                    sw.Close();
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
                return "";
            }
        }

    こんなコードにしましたら、書き込みがうまくいきました。

    でも、データテーブルのヘッダー(タイトル)行が、IDで始まっていると、正しいCSVファイルにならないみたいでした。今はその原因を探っていますが、面倒なので、タイトル(上記のヘッダー部分)はすべてダブルクォーテーションで囲ってみようかと思っています。そうすると、うまくいくようです。

    みなさんのご指導でうまくいってます。ありがとうございました。

    2013年7月15日 20:37