Как лучше всего создать CSV (текстовый файл с разделителями-запятыми) для загрузки с ASP.NET? - PullRequest
18 голосов
/ 04 сентября 2008

Это то, что у меня есть. Оно работает. Но есть ли более простой или лучший способ?

Одна страница 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

Ответы [ 8 ]

22 голосов
/ 04 сентября 2008

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 на CodeProject . Я фактически использовал его в рабочем коде, и он действительно очень быстрый и очень простой в использовании!

8 голосов
/ 04 сентября 2008

Я передаю все свои данные CSV через такую ​​функцию:

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

Кроме того, если вы не используете html, вам, вероятно, нужен обработчик http (файл .as h x), а не полная веб-страница. Если вы создаете новый обработчик в Visual Studio, скорее всего, вы просто скопируете свой существующий код в метод main, и он просто сработает, с небольшим приростом производительности для ваших усилий.

4 голосов
/ 19 сентября 2008

Вы можете создать эквивалент 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();
    }
}
3 голосов
/ 04 сентября 2008

Если вам нужен преобразователь значений с разделителями-двоеточиями, то существует сторонний открытый исходный код с именем FileHelpers . Я не уверен, под какой лицензией он находится, но это мне очень помогло.

2 голосов
/ 05 сентября 2008

С классом Page связано много накладных расходов. Поскольку вы просто выплевываете файл CSV и не нуждаетесь в обратной передаче, серверных элементах управления, кэшировании или прочем, вы должны превратить это в обработчик с расширением .ashx. Смотрите здесь .

1 голос
/ 23 августа 2013

Выглядит в основном хорошо, за исключением вашей функции "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 для тех строк, которые, как вы знаете, не содержат символов новой строки (ввод из простых однострочных текстовых форм и т. Д.).

1 голос
/ 28 октября 2010

Я использую следующий метод при построении файла CSV из DataTable. 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;
        }
1 голос
/ 04 сентября 2008

В дополнение к тому, что сказал Саймон, вы можете прочитать руководство CSV и убедиться, что ваш вывод не распространяется ни на одну из ошибок.

Чтобы прояснить что-то, Саймон сказал:

Тогда окружите это кавычками, если хотите

Поля, которые содержат двойные кавычки (""), должны быть полностью окружены двойными кавычками. Не должно быть никакого вреда, если просто обернуть все поля двойными кавычками, если только вы специально не хотите, чтобы анализатор удалял начальные и конечные пробелы (вместо того, чтобы обрезать его самостоятельно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...