哎呀.涉及 CSV 文件的读取器和换行符问题



我有一个应用程序,它处理通过 RabbitMQ 从许多不同的上游应用程序交付的 CSV——通常每个文件 5000-15,000 行。大多数时候它工作得很好。然而,这些上游应用程序中有几个是旧的(12-15年(,编写它们的人早已不在了。

由于换行符,我无法从这些较旧的应用程序中读取 CSV 文件。我发现这有点奇怪,因为换行符可以看到映射到 UTF-8 回车符 (http://www.fileformat.info/info/unicode/char/000d/index.htm(。通常,应用仅读取这些旧文件中的标头,而不读取其他任何内容。

如果我在文本编辑器中打开其中一个文件并另存为 utf-8 编码覆盖现有文件,那么它完全没有问题。

我尝试过的事情我希望有效:

-使用阅读器:

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
}

-使用扫描仪逐行读取(获取布菲奥。扫描仪:令牌太长(

scanner := bufio.NewScanner(file)
var bb bytes.Buffer
for scanner.Scan() {
bb.WriteString(fmt.Sprintf("%sn", 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 文件。

对于任何偶然发现这一点并想要一个不涉及strings.Replace的答案的人,这里有一个包装io的方法。用于替换单独回车的读卡器。它可能更有效率,但与基于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 rn 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
}

您是否尝试过将所有行尾从\r或\r替换到?

最新更新