ASP.NET でダウンロードするための CSV (カンマ区切りテキスト ファイル) を生成するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/44194

  •  09-06-2019
  •  | 
  •  

質問

これが私が持っているものです。それは動作します。しかし、もっと簡単な、またはより良い方法はあるのでしょうか?

ASPX ページにダウンロード リンクがあります...

<asp:HyperLink ID="HyperLinkDownload" runat="server" NavigateUrl="~/Download.aspx">Download as CSV file</asp:HyperLink>

そして、Download.aspx.vb コードビハインドを入手しました...

Public Partial Class Download
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        'set header
        Response.Clear()
        Response.ContentType = "text/csv"
        Dim FileName As String = "books.csv"
        Response.AppendHeader("Content-Disposition", "attachment;filename=" + FileName)

        'generate file content
        Dim db As New bookDevelopmentDataContext
        Dim Allbooks = From b In db.books _
                       Order By b.Added _
                       Select b
        Dim CsvFile As New StringBuilder
        CsvFile.AppendLine(CsvHeader())
        For Each b As Book In Allbooks
            CsvFile.AppendLine(bookString(b))
        Next

        'write the file
        Response.Write(CsvFile.ToString)
        Response.End()
    End Sub

    Function CsvHeader() As String
        Dim CsvLine As New StringBuilder
        CsvLine.Append("Published,")
        CsvLine.Append("Title,")
        CsvLine.Append("Author,")
        CsvLine.Append("Price")
        Return CsvLine.ToString
    End Function

    Function bookString(ByVal b As Book) As String
        Dim CsvLine As New StringBuilder
        CsvLine.Append(b.Published.ToShortDateString + ",")
        CsvLine.Append(b.Title.Replace(",", "") + ",")
        CsvLine.Append(b.Author.Replace(",", "") + ",")
        CsvLine.Append(Format(b.Price, "c").Replace(",", ""))
        Return CsvLine.ToString
    End Function

End Class
役に立ちましたか?

解決

CSV 形式には注意点がいくつかあります。次のような質問を自分自身に問いかけたことはありますか?

  • データにカンマが埋め込まれていますか?
  • データに二重引用符が埋め込まれていますか?
  • 私のデータには改行が含まれていますか?
  • Unicode 文字列をサポートする必要がありますか?

上記のコードにはいくつかの問題があることがわかります。まずカンマの事ですが…カンマを削除しています:

CsvLine.Append(Format(b.Price, "c").Replace(",", ""))

なぜ?CSV では、カンマがあるものはすべて引用符で囲む必要があります。

CsvLine.Append(String.Format("\"{0:c}\"", b.Price))

(あるいはそのようなもの...私の VB はあまり良くありません)。コンマがあるかどうかわからない場合は、引用符で囲んでください。文字列に引用符が含まれている場合は、引用符を 2 つ重ねてエスケープする必要があります。 " になる "".

b.Title.Replace("\"", "\"\"")

次に、必要に応じてこれを引用符で囲みます。文字列に改行がある場合は、文字列を引用符で囲む必要があります。はい、文字通りの改行です CSV ファイルで許可されます。人間にとっては奇妙に見えますが、それでも大丈夫です。

優れた CSV ライターを作成するには、ある程度の考慮が必要です。優れた CSV リーダー (パーサー) は、単純に難しいものです (いや、CSV を解析するには正規表現が十分ではありません...)目的地の約 95% までしか到達できません)。

あとはユニコードとか…より一般的には I18N (国際化) の問題です。たとえば、書式設定された価格からカンマを削除するとします。ただし、これは価格が米国で期待どおりに設定されていることを前提としています。フランスでは、数値の書式設定が逆になります (コンマの代わりにピリオドが使用され、 逆に)。結論としては、可能な限り文化に依存しない書式設定を使用することです。

ここでの問題は 生成する CSV の場合、必然的に CSV を解析する必要があります。.NET では、私が見つけた (無料の) 最良のパーサーは次のとおりです。 高速 CSV リーダー の上 コードプロジェクト. 。実際に製品コードで使用しましたが、本当に高速で、非常に使いやすいです。

他のヒント

すべての CSV データを次のような関数を通じて渡します。

Function PrepForCSV(ByVal value As String) As String
    return String.Format("""{0}""", Value.Replace("""", """"""))
End Function

また、HTML を提供していない場合は、おそらく http ハンドラー (.ash.x ファイル)、完全な Web ページではなく。Visual Studio で新しいハンドラーを作成する場合、既存のコードを main メソッドにコピーするだけで正常に動作し、その労力に見合ったパフォーマンスの向上がわずかに得られる可能性があります。

クエリ自体で bookString() と同等のものを作成できます。ここでは、より簡単な方法だと思うものを示します。

protected void Page_Load(object sender, EventArgs e)
{
    using (var db = new bookDevelopmentDataContext())
    {
        string fileName = "book.csv";
        var q = from b in db.books
                select string.Format("{0:d},\"{1}\",\"{2}\",{3:F2}", b.Published, b.Title.Replace("\"", "\"\""), b.Author.Replace("\"", "\"\""), t.price);

        string outstring = string.Join(",", q.ToArray());

        Response.Clear();
        Response.ClearHeaders();
        Response.ContentType = "text/csv";
        Response.AppendHeader("Content-Disposition", string.Format("attachment;filename={0}", fileName));
        Response.Write("Published,Title,Author,Price," + outstring);
        Response.End();
    }
}

コロンで区切られた値コンバーターが必要な場合は、サードパーティのオープンソースと呼ばれるものがあります。 ファイルヘルパー. 。どのようなオープンソース ライセンスに基づいているのかはわかりませんが、非常に役に立ちました。

Page クラスには多くのオーバーヘッドが関連付けられています。CSV ファイルを吐き出すだけで、ポストバック、サーバー コントロール、キャッシュなどは必要ないため、これを .ashx 拡張子の付いたハンドラーにする必要があります。 ここを参照.

Simon の発言に加えて、次の記事もお読みください。 CSV の使い方ガイド そして、出力が問題点に遭遇しないようにしてください。

サイモンが言ったことを明確にするために:

次に、必要に応じてこれを引用符で囲みます

二重引用符 ("") が含まれるフィールドは、完全に二重引用符で囲む必要があります。特にパーサーで先頭と末尾の空白を (自分でトリミングするのではなく) 取り除きたい場合を除き、すべてのフィールドを二重引用符で囲むだけでも問題はありません。

DataTable から CSV ファイルを構築する場合は、次の方法を使用します。ControllerContext は、ファイルが書き込まれる単なる応答ストリーム オブジェクトです。あなたにとって、それは単なる Response オブジェクトになります。

public override void ExecuteResult(ControllerContext context)
        {
            StringBuilder csv = new StringBuilder(10 * Table.Rows.Count * Table.Columns.Count);

            for (int c = 0; c < Table.Columns.Count; c++)
            {
                if (c > 0)
                    csv.Append(",");
                DataColumn dc = Table.Columns[c];
                string columnTitleCleaned = CleanCSVString(dc.ColumnName);
                csv.Append(columnTitleCleaned);
            }
            csv.Append(Environment.NewLine);
            foreach (DataRow dr in Table.Rows)
            {
                StringBuilder csvRow = new StringBuilder();
                for(int c = 0; c < Table.Columns.Count; c++)
                {
                    if(c != 0)
                        csvRow.Append(",");

                    object columnValue = dr[c];
                    if (columnValue == null)
                        csvRow.Append("");
                    else
                    {
                        string columnStringValue = columnValue.ToString();


                        string cleanedColumnValue = CleanCSVString(columnStringValue);

                        if (columnValue.GetType() == typeof(string) && !columnStringValue.Contains(","))
                        {
                            cleanedColumnValue = "=" + cleanedColumnValue; // Prevents a number stored in a string from being shown as 8888E+24 in Excel. Example use is the AccountNum field in CI that looks like a number but is really a string.
                        }
                        csvRow.Append(cleanedColumnValue);
                    }
                }
                csv.AppendLine(csvRow.ToString());
            }

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = "text/csv";
            response.AppendHeader("Content-Disposition", "attachment;filename=" + this.FileName);
            response.Write(csv.ToString());
        }

        protected string CleanCSVString(string input)
        {
            string output = "\"" + input.Replace("\"", "\"\"").Replace("\r\n", " ").Replace("\r", " ").Replace("\n", "") + "\"";
            return output;
        }

関数「BookString()」を除いて、ほとんど良好に見えますが、最初にこれらの文字列をすべて次のような小さな関数に渡す必要があります。

Private Function formatForCSV(stringToProcess As String) As String
    If stringToProcess.Contains("""") Or stringToProcess.Contains(",") Then
        stringToProcess = String.Format("""{0}""", stringToProcess.Replace("""", """"""))
    End If
    Return stringToProcess
End Function

'So, lines like this:
CsvLine.Append(b.Title.Replace(",", "") + ",")
'would be lines like this instead:
CsvLine.Append(formatForCSV(b.Title)) + ",")

この関数は、文字列を CSV 用に適切にフォーマットします。文字列に引用符またはコンマがある場合は、引用符を二重引用符に置き換え、文字列の前後に引用符を追加します。

改行は考慮されませんが、改行が含まれていないことがわかっている文字列 (単純な 1 行テキスト フォームからの入力など) に対してのみ、良好な CSV 出力が安全に保証されることに注意してください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top