Подтвердить что ты не робот

Как лучше всего создать 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
4b9b3361

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

Ответ 2

Я передаю все свои CSV-данные с помощью такой функции:

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

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

Ответ 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();
    }
}

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

Затем окружайте это кавычками, если хотите

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

Ответ 7

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

Ответ 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 для тех строк, которые, как вам известно, не имеют новых строк (входы от простых текстовых форм одной строки и т.д.).