Как мне лучше всего сгенерировать CSV (текстовый файл, разделенный запятыми) для загрузки с помощью ASP.NET?

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 имеет некоторые подводные камни.Задавали ли вы себе эти вопросы:

  • Есть ли в каких-либо моих данных встроенные запятые?
  • Есть ли в каких-либо моих данных встроенные двойные кавычки?
  • Есть ли в каких-либо моих данных новые строки?
  • Нужно ли мне поддерживать строки в Юникоде?

Я вижу несколько проблем в вашем коде выше.Прежде всего, насчет запятой...вы убираете запятые:

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

Почему?В CSV вы должны заключать в кавычки все, что имеет запятые:

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

(или что-то в этом роде...мой VB не очень хорош).Если вы не уверены, есть ли там запятые, но заключите их в кавычки.Если в строке есть кавычки, вам нужно экранировать их, удвоив. " становится "".

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

Затем, если хотите, заключите это в кавычки.Если в вашей строке есть символы перевода строк, вам нужно заключить строку в кавычки...да, буквальный перевод строк являются разрешен в CSV-файлах.Людям это кажется странным, но все это хорошо.

Хороший редактор CSV требует некоторого обдумывания.Хороший CSV-ридер (parser) просто сложен (и нет, regex недостаточно хорош для синтаксического анализа 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-обработчик (.ashx-файл), а не полноценную веб-страницу.Если вы создадите новый обработчик в Visual Studio, скорее всего, вы могли бы просто скопировать существующий код в основной метод, и он просто заработает с небольшим повышением производительности за ваши усилия.

Вы можете создать эквивалент 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. Смотрите здесь.

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

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

Затем заключите это в кавычки, если хотите

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

Я использую следующий метод при создании CSV-файла из DataTable.ControllerContext - это просто объект потока ответа, в который записывается файл.Для вас это будет просто объект ответа.

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

Обратите внимание, что он не учитывает новые строки, но может безопасно гарантировать хороший вывод CSV только для тех строк, которые, как вы знаете, не содержат новых строк (входные данные из простых однострочных текстовых форм и т.д.).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top