2009-03-20 22 views
7

Tôi đã viết chương trình C# để đọc tệp .xls/.xlsx và đầu ra của Excel thành văn bản CSV và Unicode. Tôi đã viết một chương trình riêng để xóa các bản ghi trống. Điều này được thực hiện bằng cách đọc từng dòng với StreamReader.ReadLine(), và sau đó đi ký tự theo ký tự thông qua chuỗi và không viết dòng để xuất nếu nó chứa tất cả dấu phẩy (cho CSV) hoặc tất cả các tab (cho văn bản Unicode).C# StreamReader.ReadLine() - Cần phải chọn các ký hiệu dòng

Sự cố xảy ra khi tệp Excel chứa các dòng mới được nhúng (\ x0A) bên trong các ô. Tôi thay đổi XLS của tôi để chuyển đổi CSV để tìm những dòng mới (kể từ khi nó đi tế bào của tế bào) và viết chúng như \ x0A, và dòng bình thường chỉ cần sử dụng StreamWriter.WriteLine().

Sự cố xảy ra trong chương trình riêng biệt để xóa bản ghi trống. Khi tôi đọc với StreamReader.ReadLine(), theo định nghĩa nó chỉ trả về chuỗi với dòng, không phải là terminator. Vì các dòng mới được nhúng hiển thị dưới dạng hai dòng riêng biệt, tôi không thể cho biết đó là bản ghi đầy đủ và đó là một dòng mới được nhúng cho khi tôi viết chúng vào tệp cuối cùng.

Tôi thậm chí không chắc mình có thể đọc trong \ x0A vì mọi thứ trên đầu vào đều đăng ký là '\ n'. Tôi có thể đi nhân vật theo nhân vật, nhưng điều này phá hủy logic của tôi để loại bỏ các dòng trống.

Bất kỳ ý tưởng nào cũng sẽ được đánh giá cao.

Trả lời

13

Tôi khuyên bạn nên thay đổi kiến ​​trúc của mình để hoạt động giống như trình phân tích cú pháp trong trình biên dịch.

Bạn muốn tạo một từ khóa trả về một chuỗi mã thông báo, sau đó là trình phân tích cú pháp đọc chuỗi mã thông báo và thực hiện các công cụ với chúng.

Trong trường hợp của bạn các thẻ sẽ là:

  1. dữ liệu Cột
  2. Comma
  3. End of Line

Bạn sẽ đối xử với '\ n' ('\ x0a') bằng bản thân nó như là một dòng mới được nhúng, và do đó bao gồm nó như một phần của một mã thông báo cột. Một '\ r \ n' sẽ tạo thành một mã thông báo End of Line.

này có những ưu điểm của:

  1. Làm chỉ có 1 đường chuyền so với dữ liệu
  2. Chỉ lưu trữ tối đa là 1 dòng giá trị của dữ liệu
  3. Tái sử dụng càng nhiều bộ nhớ càng tốt (đối với người xây dựng chuỗi và danh sách)
  4. Thật dễ dàng để thay đổi nên yêu cầu của bạn thay đổi

Dưới đây là một ví dụ về những gì wou lexer ld trông giống như:

Tuyên bố từ chối trách nhiệm: Tôi chưa biên soạn, hãy để một mình thử nghiệm, mã này, vì vậy bạn cần dọn dẹp và đảm bảo nó hoạt động.

enum TokenType 
{ 
    ColumnData, 
    Comma, 
    LineTerminator 
} 

class Token 
{ 
    public TokenType Type { get; private set;} 
    public string Data { get; private set;} 

    public Token(TokenType type) 
    { 
     Type = type; 
    } 

    public Token(TokenType type, string data) 
    { 
     Type = type; 
     Data = data; 
    } 
} 

private IEnumerable<Token> GetTokens(TextReader s) 
{ 
    var builder = new StringBuilder(); 

    while (s.Peek() >= 0) 
    { 
     var c = (char)s.Read(); 
     switch (c) 
     { 
      case ',': 
      { 
       if (builder.Length > 0) 
       { 
        yield return new Token(TokenType.ColumnData, ExtractText(builder)); 
       } 
       yield return new Token(TokenType.Comma); 
       break; 
      } 
      case '\r': 
      { 
       var next = s.Peek(); 
       if (next == '\n') 
       { 
        s.Read(); 
       } 

       if (builder.Length > 0) 
       { 
        yield return new Token(TokenType.ColumnData, ExtractText(builder)); 
       } 
       yield return new Token(TokenType.LineTerminator); 
       break; 
      } 
      default: 
       builder.Append(c); 
       break; 
     } 

    } 

    s.Read(); 

    if (builder.Length > 0) 
    { 
     yield return new Token(TokenType.ColumnData, ExtractText(builder)); 
    } 
} 

private string ExtractText(StringBuilder b) 
{ 
    var ret = b.ToString(); 
    b.Remove(0, b.Length); 
    return ret; 
} 

của bạn "phân tích cú pháp" mã sau đó sẽ trông như thế này:

public void ConvertXLS(TextReader s) 
{ 
    var columnData = new List<string>(); 
    bool lastWasColumnData = false; 
    bool seenAnyData = false; 

    foreach (var token in GetTokens(s)) 
    { 
     switch (token.Type) 
     { 
      case TokenType.ColumnData: 
      { 
       seenAnyData = true; 
       if (lastWasColumnData) 
       { 
        //TODO: do some error reporting 
       } 
       else 
       { 
        lastWasColumnData = true; 
        columnData.Add(token.Data); 
       } 
       break; 
      } 
      case TokenType.Comma: 
      { 
       if (!lastWasColumnData) 
       { 
        columnData.Add(null); 
       } 
       lastWasColumnData = false; 
       break; 
      } 
      case TokenType.LineTerminator: 
      { 
       if (seenAnyData) 
       { 
        OutputLine(lastWasColumnData); 
       } 
       seenAnyData = false; 
       lastWasColumnData = false; 
       columnData.Clear(); 
      } 
     } 
    } 

    if (seenAnyData) 
    { 
     OutputLine(columnData); 
    } 
} 
+0

Cảm ơn một tấn Scott. Điều này trông giống như giải pháp thích hợp. Tôi là một sinh viên tốt nghiệp gần đây của CompSci nên thật tuyệt khi được giúp đỡ như thế này. –

4

Bạn không thể thay đổi StreamReader để trả về các thuật ngữ dòng và bạn không thể thay đổi những gì nó sử dụng để chấm dứt dòng.

Tôi không hoàn toàn rõ ràng về sự cố về những gì bạn đang thoát, đặc biệt là về "và viết chúng dưới dạng \ x0A". Một mẫu của tập tin có lẽ sẽ giúp ích.

Có vẻ như bạn có thể cần phải làm việc theo ký tự hoặc có thể tải toàn bộ tệp trước và thực hiện thay thế toàn cầu, ví dụ:

x.Replace("\r\n", "\u0000") // Or some other unused character 
.Replace("\n", "\\x0A") // Or whatever escaping you need 
.Replace("\u0000", "\r\n") // Replace the real line breaks 

tôi chắc chắn rằng bạn có thể làm điều đó với một regex và nó có lẽ sẽ hiệu quả hơn, nhưng tôi tìm thấy những chặng đường dài dễ hiểu hơn :) Đó là một chút của một hack phải làm một toàn cầu thay thế mặc dù - hy vọng với nhiều thông tin hơn, chúng tôi sẽ đưa ra một giải pháp tốt hơn.

+0

+1 cho "bạn có thể cần phải làm việc từng ký tự" – eglasius

+0

tôi giống như giải pháp của tôi (mà bạn có thể xem dưới đây), nhưng tôi thực sự thích phát triển các trình biên dịch, vì vậy nó có thể là một trong những "với ai đó với một cái búa tất cả mọi thứ trông giống như một móng tay" loại của sự vật –

1

Về cơ bản, trở lại trong Excel (shift + enter hoặc alt + enter, tôi không thể nhớ) đặt một dòng mới tương đương với \ x0A trong mã hóa mặc định mà tôi sử dụng để viết CSV của mình. Khi tôi viết thư cho CSV, tôi sử dụng StreamWriter.WriteLine(), kết quả đầu ra của dòng cộng với dòng mới (mà tôi tin là \ r \ n).

CSV là tốt và đi ra chính xác như thế nào Excel sẽ lưu nó, vấn đề là khi tôi đọc nó vào loại bỏ hồ sơ trống, tôi đang sử dụng ReadLine() sẽ xử lý một bản ghi với một dòng mới nhúng như một CRLF .

Dưới đây là một ví dụ về các tập tin sau khi tôi chuyển sang CSV ...

Reference,Name of Individual or Entity,Type,Name Type,Date of Birth,Place of Birth,Citizenship,Address,Additional Information,Listing Information,Control Date,Committees 
1050,"Aziz Salih al-Numan 
",Individual,Primary Name,1941 or 1945,An Nasiriyah,Iraqi,,Ba’th Party Regional Command Chairman; Former Governor of Karbala and An Najaf Former Minister of Agriculture and Agrarian Reform (1986-1987),Resolution 1483 (2003),6/27/2003,1518 (Iraq) 
1050a,???? ???? ???????,Individual,Original script,1941 or 1945,An Nasiriyah,Iraqi,,Ba’th Party Regional Command Chairman; Former Governor of Karbala and An Najaf Former Minister of Agriculture and Agrarian Reform (1986-1987),Resolution 1483 (2003),6/27/2003,1518 (Iraq) 

Như bạn thấy, hồ sơ đầu tiên có một mới-line nhúng sau khi al-Numan. Khi tôi sử dụng ReadLine(), tôi nhận được '1050,' Aziz Salih al-Numan 'và khi tôi viết nó ra, WriteLine() kết thúc dòng đó với một CRLF. Tôi mất đi dòng kết thúc ban đầu. Khi tôi sử dụng ReadLine() một lần nữa Tôi có thể đọc toàn bộ tập tin trong và thay thế chúng, nhưng sau đó tôi phải thay thế chúng trở lại sau đó. Về cơ bản những gì tôi muốn làm là lấy trình kết thúc dòng xác định nếu nó \ x0a hoặc CRLF, và sau đó nếu \ x0A của nó, tôi sẽ sử dụng Write() và chèn terminator đó.

0

Tôi biết tôi là một chút muộn để các trò chơi ở đây, nhưng tôi đã có cùng một vấn đề và giải pháp của tôi là một đơn giản hơn nhiều so với nhất định.

Nếu bạn có thể xác định số cột nên dễ làm vì dòng đầu tiên thường là tiêu đề cột, bạn có thể kiểm tra số cột của mình dựa vào số cột dự kiến. Nếu số cột không bằng số cột dự kiến, bạn chỉ cần ghép nối dòng hiện tại với các dòng chưa từng có trước đây. Ví dụ:

string sep = "\",\""; 
int columnCount = 0; 
while ((currentLine = sr.ReadLine()) != null) 
{ 
    if (lineCount == 0) 
    { 
     lineData = inLine.Split(new string[] { sep }, StringSplitOptions.None); 
     columnCount = lineData.length; 
     ++lineCount; 
     continue; 
    } 
    string thisLine = lastLine + currentLine; 

    lineData = thisLine.Split(new string[] { sep }, StringSplitOptions.None); 
    if (lineData.Length < columnCount) 
    { 
     lastLine += currentLine; 
     continue; 
    } 
    else 
    { 
     lastLine = null; 
    } 
    ...... 
0

Cảm ơn bạn rất nhiều với mã của bạn và một số người khác tôi đã đưa ra giải pháp sau! Tôi đã thêm một liên kết ở phía dưới để một số mã tôi đã viết rằng sử dụng một số logic từ trang này. Tôi nghĩ tôi sẽ vinh danh nơi danh dự đã đến! Cảm ơn!

Dưới đây là giải thích về những gì tôi cần: Hãy thử Điều này, tôi đã viết điều này vì tôi có một số rất lớn '|' các tệp phân tách có \ r \ n bên trong một số cột và tôi cần sử dụng \ r \ n làm phần cuối của dấu tách dòng. Tôi đã cố gắng để nhập khẩu một số tập tin bằng cách sử dụng các gói SSIS nhưng vì một số dữ liệu bị hỏng trong các tập tin tôi đã không thể. Tệp lớn hơn 5 GB nên tệp quá lớn để mở và khắc phục thủ công. Tôi tìm thấy câu trả lời thông qua việc xem qua rất nhiều diễn đàn để hiểu cách hoạt động của luồng và kết thúc với giải pháp đọc từng ký tự trong tệp và trích ra dòng dựa trên định nghĩa tôi đã thêm vào.điều này là để sử dụng trong một ứng dụng dòng lệnh, hoàn thành với trợ giúp :). Tôi hy vọng điều này sẽ giúp một số người khác ra, tôi đã không tìm thấy một giải pháp khá giống như nó ở bất cứ nơi nào khác, mặc dù những ý tưởng được lấy cảm hứng từ diễn đàn này và những người khác.

https://stackoverflow.com/a/12640862/1582188

Các vấn đề liên quan