Wie erstelle ich am besten eine CSV-Datei (durch Kommas getrennte Textdatei) zum Herunterladen mit ASP.NET?

StackOverflow https://stackoverflow.com/questions/44194

  •  09-06-2019
  •  | 
  •  

Frage

Das ist es, was ich habe.Es klappt.Aber gibt es einen einfacheren oder besseren Weg?

Auf einer ASPX-Seite habe ich den Download-Link ...

<asp:HyperLink ID="HyperLinkDownload" runat="server" NavigateUrl="~/Download.aspx">Download as CSV file</asp:HyperLink>

Und dann habe ich den Download.aspx.vb-Code dahinter ...

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
War es hilfreich?

Lösung

Bei der CSV-Formatierung gibt es einige Fallstricke.Haben Sie sich diese Fragen gestellt:

  • Sind in meinen Daten Kommas eingebettet?
  • Sind in meinen Daten doppelte Anführungszeichen eingebettet?
  • Haben meine Daten Zeilenumbrüche?
  • Muss ich Unicode-Strings unterstützen?

Ich sehe mehrere Probleme in Ihrem Code oben.Die Sache mit dem Komma zuerst...Sie entfernen Kommas:

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

Warum?In CSV sollten Sie alles, was Kommas enthält, in Anführungszeichen setzen:

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

(oder etwas ähnliches...mein VB ist nicht sehr gut).Wenn Sie nicht sicher sind, ob es Kommas gibt, setzen Sie es doch in Anführungszeichen.Wenn die Zeichenfolge Anführungszeichen enthält, müssen Sie diese durch Verdoppelung maskieren. " wird "".

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

Wenn Sie möchten, können Sie dies dann in Anführungszeichen setzen.Wenn Ihre Zeichenfolge Zeilenumbrüche enthält, müssen Sie die Zeichenfolge in Anführungszeichen setzen ...ja, wörtliche Zeilenumbrüche Sind in CSV-Dateien erlaubt.Für Menschen sieht es seltsam aus, aber es ist alles gut.

Ein guter CSV-Autor erfordert einige Überlegungen.Ein guter CSV-Reader (Parser) ist einfach schwierig (und nein, Regex ist nicht gut genug zum Parsen von CSV ...Sie werden damit nur etwa 95 % des Weges zurücklegen.

Und dann ist da noch Unicode...oder allgemeiner I18N-Themen (Internationalisierung).Beispielsweise entfernen Sie Kommas aus einem formatierten Preis.Dies setzt jedoch voraus, dass der Preis so formatiert ist, wie Sie es in den USA erwarten.In Frankreich ist die Zahlenformatierung umgekehrt (Punkte anstelle von Kommas usw.). und umgekehrt).Fazit: Verwenden Sie nach Möglichkeit kulturunabhängige Formatierungen.

Während das Problem hier ist Erstellen CSV, zwangsläufig müssen Sie CSV analysieren.In .NET ist der beste Parser, den ich gefunden habe (kostenlos). Schneller CSV-Reader An CodeProjekt.Ich habe es tatsächlich im Produktionscode verwendet und es ist wirklich sehr, sehr schnell und sehr einfach zu verwenden!

Andere Tipps

Ich übergebe alle meine CSV-Daten über eine Funktion wie diese:

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

Wenn Sie kein HTML bereitstellen, benötigen Sie wahrscheinlich auch einen http-Handler (.as).Hx-Datei) anstelle einer vollständigen Webseite.Wenn Sie in Visual Studio einen neuen Handler erstellen, ist die Wahrscheinlichkeit groß, dass Sie einfach Ihren vorhandenen Code in die Hauptmethode kopieren und es funktioniert, mit einer kleinen Leistungssteigerung für Ihre Bemühungen.

Sie können das Äquivalent von bookString() in der Abfrage selbst erstellen.Hier ist, was meiner Meinung nach ein einfacherer Weg wäre.

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

Wenn Sie einen durch Doppelpunkte getrennten Wertekonverter benötigen, gibt es eine Open-Source-Lösung eines Drittanbieters namens FileHelpers.Ich bin mir nicht sicher, unter welcher Open-Source-Lizenz es steht, aber es hat mir sehr geholfen.

Mit der Page-Klasse ist viel Overhead verbunden.Da Sie lediglich eine CSV-Datei ausspucken und kein Postback, keine Serversteuerung, kein Caching oder dergleichen benötigen, sollten Sie daraus einen Handler mit der Erweiterung .ashx machen. Siehe hier.

Zusätzlich zu dem, was Simon gesagt hat, möchten Sie vielleicht das lesen CSV-Anleitung und stellen Sie sicher, dass Ihre Ausgabe nicht auf irgendwelche Fallstricke stößt.

Um etwas klarzustellen, sagte Simon:

Wenn Sie möchten, können Sie dies dann in Anführungszeichen setzen

Felder, die doppelte Anführungszeichen („“) enthalten, müssen vollständig in doppelte Anführungszeichen eingeschlossen werden.Es sollte nicht schaden, alle Felder einfach in doppelte Anführungszeichen zu setzen, es sei denn, Sie möchten ausdrücklich, dass der Parser führende und nachfolgende Leerzeichen entfernt (anstatt sie selbst zu kürzen).

Ich verwende die folgende Methode, wenn ich eine CSV-Datei aus einer DataTable erstelle.ControllerContext ist lediglich das Antwortstreamobjekt, in das die Datei geschrieben wird.Für Sie wird es einfach das Response-Objekt sein.

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;
        }

Sieht größtenteils gut aus, außer dass Sie in Ihrer Funktion „BookString()“ alle diese Zeichenfolgen zuerst durch eine kleine Funktion wie diese übergeben sollten:

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)) + ",")

Die Funktion formatiert Ihre Zeichenfolgen gut für CSV.Es ersetzt Anführungszeichen durch doppelte Anführungszeichen und fügt Anführungszeichen um die Zeichenfolge hinzu, wenn die Zeichenfolge entweder Anführungszeichen oder Kommas enthält.

Beachten Sie, dass es keine Zeilenumbrüche berücksichtigt, sondern nur eine sichere CSV-Ausgabe für die Zeichenfolgen gewährleisten kann, von denen Sie wissen, dass sie keine Zeilenumbrüche enthalten (Eingaben aus einfachen einzeiligen Textformularen usw.).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top