2017-07-06 90 views
0

我有一個應用程序處理CSV通過RabbitMQ從多個不同的上游應用程序交付 - 通常每個文件有5000-15,000行。大多數情況下,它的效果很好。然而,這些上游應用程序中有一些是舊的(12 - 15年),寫這些應用程序的人早已不在。涉及CSV文件的io.Reader和換行符問題

由於換行符,我無法從這些較舊的應用程序中讀取CSV文件。我發現這有點奇怪,因爲換行符會映射到UTF-8回車換行(http://www.fileformat.info/info/unicode/char/000d/index.htm)。通常,應用程序只讀取來自這些較舊文件的頭文件,而不是其他文件。

如果我在文本編輯器中打開其中一個文件並保存爲utf-8編碼,覆蓋退出的文件,那麼它完全沒有問題。

事情我已經試過我預期工作:

- 使用一個Reader:

ba := make([]byte, 262144000) 
    if _, err := file.Read(ba); err != nil { 
     return nil, err 
    } 
    ba = bytes.Trim(ba, "\x00") 
    bb := bytes.NewBuffer(ba) 
    reader := csv.NewReader(bb) 
    records, err := reader.ReadAll() 
    if err != nil { 
     return nil, err 
    } 

- 使用的掃描器讀取逐行(獲得bufio.Scanner:令牌太長)

scanner := bufio.NewScanner(file) 
    var bb bytes.Buffer 
    for scanner.Scan() { 
     bb.WriteString(fmt.Sprintf("%s\n", scanner.Text())) 
    } 

    // check for errors 
    if err = scanner.Err(); err != nil { 
     return nil, err 
    } 


reader := csv.NewReader(&bb) 
records, err := reader.ReadAll() 
if err != nil { 
    return nil, err 
} 

事情我想我的預期沒有工作(並沒有):

  • 將文件內容寫入新文件(.txt)並重新讀取文件(包括針對創建的txt文件運行dos2unix)
  • 將文件讀入標準字符串(希望Go的UTF-8編碼會奇蹟般地在其中啓動當然這並不)
  • 閱讀文件,以符文切片,然後通過字節片轉換爲字符串

我知道了https://godoc.org/golang.org/x/text/transform包,但不太確定一個可行的辦法 - 它看起來像需要知道src編碼的轉換。

我愚蠢地忽略了什麼?是否有任何建議如何將這些文件轉換爲UTF-8或在不知道文件編碼的情況下更新行結尾,同時保持應用程序對所有其他有效CSV文件的傳輸工作?有沒有任何選項,不涉及到字節和字節做字節。更換我沒有考慮過? 我希望我忽略了一些非常明顯的東西。

道歉 - 我無法共享CSV文件,原因很明顯。

+0

「道歉 - 我不能共享的CSV文件的原因很明顯。」沒有必要道歉。但是,如果您希望獲得幫助,您必須提供CSV文件的人工/匿名/清理/剝離示例。 – Volker

+0

這就是問題 - 我不能。只要我編輯或試圖將數據傳輸到另一個文件,它保存爲utf-8並且可以工作。我不認爲StackOverflow上有任何「必須」的東西 - 人們通常非常有幫助。 – Airomega

+0

另外 - 我不認爲有反正即使我可以在StackOverflow上附加文件。我必須添加到某個外部網站 - 我知道我不會從外部網站上下載一個隨機文件,由陌生人上傳。 – Airomega

回答

1

您是否嘗試將\ r \ n或\ r的所有行結尾替換爲\ n?

+0

你是一個寶石 - 工作。 s = strings.Replace(s,「\ r」,「\ n」,-1)。 – Airomega

2

對於任何人無意中發現並想要一個不涉及strings.Replace的答案,下面是一個包裝一個io.Reader以取代單獨回車的方法。它可能更高效,但對於大型文件比基於strings.Replace的解決方案效果更好。

https://gist.github.com/b5/78edaae9e6a4248ea06b45d089c277d6

// ReplaceSoloCarriageReturns wraps an io.Reader, on every call of Read it 
// for instances of lonely \r replacing them with \r\n before returning to the end customer 
// lots of files in the wild will come without "proper" line breaks, which irritates go's 
// standard csv package. This'll fix by wrapping the reader passed to csv.NewReader: 
// rdr, err := csv.NewReader(ReplaceSoloCarriageReturns(r)) 
// 
func ReplaceSoloCarriageReturns(data io.Reader) io.Reader { 
    return crlfReplaceReader{ 
     rdr: bufio.NewReader(data), 
    } 
} 

// crlfReplaceReader wraps a reader 
type crlfReplaceReader struct { 
    rdr *bufio.Reader 
} 

// Read implements io.Reader for crlfReplaceReader 
func (c crlfReplaceReader) Read(p []byte) (n int, err error) { 
    if len(p) == 0 { 
     return 
    } 

    for { 
     if n == len(p) { 
      return 
     } 

     p[n], err = c.rdr.ReadByte() 
     if err != nil { 
      return 
     } 

     // any time we encounter \r & still have space, check to see if \n follows 
     // if next char is not \n, add it in manually 
     if p[n] == '\r' && n < len(p) { 
      if pk, err := c.rdr.Peek(1); (err == nil && pk[0] != '\n') || (err != nil && err.Error() == io.EOF.Error()) { 
       n++ 
       p[n] = '\n' 
      } 
     } 

     n++ 
    } 
    return 
}