2008-09-04 8 views
18

これは私が持っているものです。できます。しかし、よりシンプルで良い方法がありますか?ASP.NETでCSV(カンマ区切りのテキストファイル)をダウンロードするのに最適な方法を教えてください。

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フォーマットにはいくつかの問題があります。

  • 自分のデータにカンマが埋め込まれていますか?
  • 私のデータに二重引用符が埋め込まれていますか?
  • 私のデータに改行がありますか?
  • ユニコード文字列をサポートする必要がありますか?

上記のコードにいくつかの問題があります。コンマの最初のものは...カンマを取り除いています:

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

なぜですか?

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

(またはそのような何か...私のVBは非常に良いではありません):CSVでは、あなたは引用符でカンマを持っている何かを囲む必要があります。カンマがあるかどうかわからない場合は、カンマを置いてください。文字列に引用符がある場合は、引用符を倍にしてエスケープする必要があります。 """になります。

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

この場合、必要に応じて引用符で囲みます。あなたの文字列に改行がある場合は、その文字列を引用符で囲む必要があります...はい、改行改行は、CSVファイルではです。それは人間には変わっているようですが、それはすべていいです。

良いCSVライターは、いくつか考える必要があります。良いCSVリーダー(パーサー)は単なる固いものです(そして、CSVを解析するのに十分ではない正規表現はありません。そこには約95%しか得られません)。

そして、Unicode ...またはより一般的にはI18N(国際化)の問題があります。たとえば、フォーマットされた価格からコンマを取り除いているとします。しかし、それはあなたが米国でそれを期待するように価格がフォーマットされていると仮定しています。フランスでは、数値書式が逆になります(コンマの代わりにピリオドが使用され、の逆の場合は)。結論として、可能な限り、文化に依存しない書式設定を使用する。

ここでの問題はですが、 CSVを生成していますが、必然的にCSVを解析する必要があります。 .NETでは、私が見つけた最良のパーサーはFast CSV ReaderCodeProject)です。私は実際にプロダクションコードでそれを使用しました、そして、それは本当に速く、非常に使いやすいです!

+0

正規表現は問題ありません。フィールドを囲む引用符を区切り文字の一部として考えることができるため、繰り返しパターンが可能です。最大の鍵は、見積もりを利用して得ることができる完全な行を確認することです。これはおそらく約95%を意味していますか? –

8

私はこのような機能を介してすべての私のCSVデータを渡す:あなたがHTMLを提供していない場合

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

はまた、あなたはおそらく(HTTPハンドラをしたいです。完全なWebページではなくh xファイル)。 Visual Studioで新しいハンドラを作成した場合、既存のコードをメインメソッドにコピーするだけで、作業効率が少し向上します。

1

Simonが言ったことに加えて、CSV how-to guideを読んで、あなたの出力がどんな邪魔になっても実行されないようにすることができます。サイモンは言った何か明確にする

を次にあなたがダブルアップ、二重引用符(「」)を含んでい

フィールドをしたい場合は、引用符でこれを包囲は完全に二重引用符で囲む必要があります。パーサーが先頭と末尾の空白を取り除きたい場合を除き、すべてのフィールドを二重引用符で囲むだけで害はありません。

3

コロンで区切られた値コンバータを使用する場合は、FileHelpersというサードパーティのオープンソースがあります。私は、オープンソースのライセンスについてはわかっていないが、それは私をかなり助けてくれた。

2

Pageクラスに関連するオーバーヘッドが大きくなります。あなたは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

私は、DataTableからCSVファイルを構築する際に以下の方法を使用します。 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のためにもあなたの文字列をフォーマットします。引用符を二重引用符で置き換え、文字列に引用符またはカンマがある場合は、引用符を引用符で囲みます。

改行は考慮されませんが、改行がないことがわかっている文字列(単純な1行のテキストフォームなどからの入力)に対しては、CSV出力を安全に保証することができます。

関連する問題