ASP.NET으로 다운로드할 CSV(쉼표로 구분된 텍스트 파일)를 가장 잘 생성하려면 어떻게 해야 합니까?

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 리더(파서)는 정말 어렵습니다(아니요, 정규식은 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 핸들러(.as)가 필요할 것입니다.시간x 파일) 전체 웹페이지가 아닌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();
    }
}

콜론으로 구분된 값 변환기를 원한다면 다음과 같은 타사 오픈 소스가 있습니다. FileHelpers.어떤 오픈 소스 라이센스가 적용되는지는 잘 모르겠지만 꽤 많은 도움이 되었습니다.

Page 클래스와 관련된 오버헤드가 많습니다.CSV 파일을 내보내는 것이므로 포스트백, 서버 제어, 캐싱 등이 필요하지 않으므로 이 파일을 .ashx 확장자를 가진 핸들러로 만들어야 합니다. 여기를 보아라.

사이먼이 말한 것 외에도 다음 내용을 읽어 볼 수도 있습니다. CSV 사용법 가이드 그리고 출력이 문제를 해결하지 않는지 확인하세요.

Simon이 말한 내용을 명확히 하기 위해 다음과 같이 말했습니다.

그런 다음 원하는 경우 이를 따옴표로 묶습니다.

큰따옴표("")가 포함된 필드는 큰따옴표로 완전히 묶어야 합니다.파서가 선행 및 후행 공백을 제거하도록 특별히 원하지 않는 한(직접 잘라내는 대신) 모든 필드를 큰따옴표로 묶는 것만으로는 아무런 해가 없어야 합니다.

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

"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