2008-09-04 1 views
18

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

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

ответ

22

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

+0

Regexes отлично - тот факт, что кавычки, окружающие поле, можно рассматривать как часть разделителя, позволяют повторять шаблон. Самый большой ключ - убедиться, что у вас есть полная линия, которую вы можете получить с помощью подсчета котировок - возможно, это то, что вы имели в виду примерно на 95%? –

8

Я прохожу все мои данные CSV через функцию следующим образом:

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

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

1

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

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

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

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

3

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

2

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

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(); 
    } 
} 
+0

спасибо за сообщение, это помогло решить проблему с firefox [здесь] (http://stackoverflow.com/a/32373057/2218697) - это решение – stom

1

Я использую следующий метод при создании 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

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

Смежные вопросы