18

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

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

8 回答 8

23

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 中,我发现(免费)最好的解析器是CodeProject上的Fast CSV Reader。我实际上已经在生产代码中使用了它,它真的非常快,而且非常易于使用!

于 2008-09-04T17:19:59.437 回答
9

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

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

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

于 2008-09-04T17:26:20.890 回答
4

您可以在查询本身中创建 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();
    }
}
于 2008-09-18T23:53:38.250 回答
3

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

于 2008-09-04T17:55:45.700 回答
2

除了 Simon 所说的之外,您可能还想阅读CSV 操作指南并确保您的输出不会遇到任何问题。

为了澄清西蒙所说的:

如果你愿意,然后用引号括起来

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

于 2008-09-04T17:33:56.423 回答
2

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

于 2008-09-05T18:32:03.903 回答
1

从 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;
        }
于 2010-10-28T19:23:52.243 回答
1

除了在你的函数“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 输出。

于 2013-08-23T13:45:10.193 回答