【问题标题】:How do I best generate a CSV (comma-delimited text file) for download with ASP.NET?如何最好地生成 CSV(逗号分隔的文本文件)以供 ASP.NET 下载?
【发布时间】:2010-09-07 20:03:17
【问题描述】:

这就是我所拥有的。有用。但是,有没有更简单或更好的方法?

一个 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

【问题讨论】:

    标签: asp.net vb.net file-io csv


    【解决方案1】:

    CSV 格式有一些问题。你有没有问过自己这些问题:

    • 我的任何数据是否都嵌入了逗号?
    • 我的任何数据中是否嵌入了双引号?
    • 我的任何数据是否有换行符?
    • 我需要支持 Unicode 字符串吗?

    我在上面的代码中发现了几个问题。首先是逗号...您正在剥离逗号:

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

    为什么?在 CSV 中,您应该将任何包含逗号的内容括起来:

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

    (或类似的东西......我的VB不是很好)。如果您不确定是否有逗号,请在其周围加上引号。如果字符串中有引号,则需要通过将它们加倍来转义它们。 " 变为 ""

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

    如果你愿意,可以用引号括起来。如果字符串中有换行符,则需要用引号将字符串括起来……是的,CSV 文件中允许使用文字换行符 。这对人类来说看起来很奇怪,但一切都很好。

    一个好的 CSV 编写器需要一些思考。一个好的 CSV 阅读器(解析器)很简单(不,正则表达式不足以解析 CSV……它只会让你完成大约 95% 的工作)。

    然后是 Unicode... 或更普遍的 I18N(国际化)问题。例如,您正在从格式化的价格中去除逗号。但这是假设价格按照您在美国的预期格式化。在法国,数字格式是相反的(使用句点代替逗号,反之亦然)。最重要的是,尽可能使用与文化无关的格式。

    虽然这里的问题是生成 CSV,但您不可避免地需要解析 CSV。在 .NET 中,我找到的最好的解析器(免费)是 Fast CSV Reader on CodeProject。我实际上已经在生产代码中使用过它,它真的非常快,而且非常易于使用!

    【讨论】:

    • 正则表达式很好 - 围绕字段的引号可以被视为分隔符的一部分这一事实允许重复模式。最大的关键是确保你有一个完整的行 - 你可以通过计算引号得到 - 也许这就是你的意思 95%?
    【解决方案2】:

    我通过这样的函数传递我所有的 CSV 数据:

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

    另外,如果您不提供 html,您可能需要一个 http 处理程序(.ashx 文件)而不是完整的网页。如果您在 Visual Studio 中创建一个新的处理程序,您很可能只需将现有代码复制到 main 方法中,它就会正常工作,并为您的工作带来一点性能提升。

    【讨论】:

      【解决方案3】:

      您可以在查询本身中创建 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();
          }
      }
      

      【讨论】:

      • 感谢您的帖子,它帮助解决了 firefox 的问题here 是解决方案
      【解决方案4】:

      如果您想要一个冒号分隔的值转换器,那么有一个名为 FileHelpers 的第三方开源。我不确定它属于什么开源许可证,但它对我帮助很大。

      【讨论】:

        【解决方案5】:

        除了 Simon 所说的之外,您可能还想阅读 CSV how-to guide 并确保您的输出没有遇到任何问题。

        澄清一下西蒙说的话:

        如果你愿意,可以用引号括起来

        包含双引号 ("") 的字段需要完全用双引号括起来。用双引号将所有字段括起来应该没有任何害处,除非您特别希望解析器去除前导和尾随空格(而不是自己修剪)。

        【讨论】:

          【解决方案6】:

          Page 类有很多开销。由于您只是吐出一个 CSV 文件并且不需要回发、服务器控制、缓存或其余部分,因此您应该将其制作成一个扩展名为 .ashx 的处理程序。 See here.

          【讨论】:

            【解决方案7】:

            从 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;
                    }
            

            【讨论】:

              【解决方案8】:

              除了在你的函数“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 格式化您的字符串。如果字符串中有引号或逗号,它将用双引号替换引号并在字符串周围添加引号。

              请注意,它不考虑换行符,但只能安全地保证那些您知道没有换行符的字符串(来自简单的单行文本表单的输入等)的良好 CSV 输出。

              【讨论】:

                猜你喜欢
                • 2014-03-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-09-24
                • 2014-01-16
                • 1970-01-01
                相关资源
                最近更新 更多