2017-07-06 2 views
0

Ich habe eine Anwendung, die CSV über RabbitMQ von vielen verschiedenen Upstream-Anwendungen geliefert wird - in der Regel 5000 bis 15.000 Zeilen pro Datei. Meistens funktioniert es großartig. Allerdings sind einige dieser Upstream-Anwendungen alt (12-15 Jahre) und die Leute, die sie geschrieben haben, sind schon lange weg.io.Reader und Zeilenumbruch Problem mit einer CSV-Datei

Ich kann CSV-Dateien aus diesen älteren Anwendungen aufgrund der Zeilenumbrüche nicht lesen. Ich finde das ein bisschen komisch, da die Zeilenumbrüche UTF-8 Carriage Returns (http://www.fileformat.info/info/unicode/char/000d/index.htm) zugeordnet sind. In der Regel liest die App nur die Header aus diesen älteren Dateien und sonst nichts ein.

Wenn ich eine dieser Dateien in einem Texteditor öffne und als UTF-8-Kodierung überschreibe, wird die existierende Datei überschrieben, dann funktioniert es ohne Probleme.

Dinge, die ich habe versucht, ich erwartet:

-Mit einen 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 
    } 

-Mit der Scanner Zeile für Zeile zu lesen (get eine bufio.Scanner: Token zu lang)

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 
} 

Dinge, die ich versuchte, ich erwartet hatte, nicht zu arbeiten (und nicht):

  • Dateiinhalt in eine neue Datei (.txt) schreiben und die Datei wieder einlesen (einschließlich der Ausführung von dos2unix gegen die erstellte TXT-Datei)
  • Datei in eine Standard-Zeichenkette lesen (in der Hoffnung, Go's UTF-8-Kodierung würde magisch treten natürlich ist es nicht)
  • Lesedatei Rune in Scheiben schneiden, dann die Umwandlung in einen String über Byte Scheibe

ich bin mir bewusst, der https://godoc.org/golang.org/x/text/transform Paket, aber nicht zu sicher sein, einen gangbaren Weg - es sieht aus wie Es muss bekannt sein, dass die src-Codierung transformiert werden muss.

Bin ich dumm etwas übersehen? Gibt es Vorschläge, wie diese Dateien in UTF-8 umgewandelt werden oder die Zeilenenden aktualisiert werden, ohne die Dateicodierung zu kennen, während die Anwendung für alle anderen gültigen CSV-Dateien, die geliefert werden, funktioniert? Gibt es irgendwelche Optionen, die mich nicht von Byte zu Byte gehen und Bytes machen. Ersetze ich nicht? Ich hoffe, dass es etwas wirklich Offensichtliches gibt, das ich übersehen habe.

Entschuldigung - Ich kann die CSV-Dateien aus offensichtlichen Gründen nicht teilen.

+0

"Apologies - Ich kann die CSV-Dateien aus offensichtlichen Gründen teilen." Es gibt keine Notwendigkeit, sich zu entschuldigen. Sie müssen jedoch ein künstliches/anonymisiertes/bereinigtes/entkleidetes Beispiel der CSV-Datei erstellen, wenn Sie Hilfe erwarten. – Volker

+0

Das ist das Problem - ich kann nicht.Sobald ich es bearbeite oder versuche, die Daten in eine andere Datei zu übertragen, speichert es als utf-8 und funktioniert. Ich denke nicht, dass es bei StackOverflow ein "Muss" gibt - die Leute sind im Allgemeinen sehr hilfreich. – Airomega

+0

Außerdem - ich glaube nicht, dass es sowieso Dateien auf StackOverflow anhängen könnte, selbst wenn ich könnte. Ich müsste zu einer externen Website hinzufügen - und ich weiß, ich würde keine zufällige Datei von einer externen Website herunterladen, die von einem Fremden im Internet hochgeladen wurde. – Airomega

Antwort

1

Haben Sie versucht, alle Zeilenendungen von \ r \ n oder \ r zu \ n zu ersetzen?

+0

Du bist ein Juwel - das hat funktioniert. s = strings.Replace (s, "\ r", "\ n", -1). – Airomega

2

Für jeden, der auf dieses Thema gestoßen ist und eine Antwort wünscht, die nicht strings.Replace beinhaltet, ist hier eine Methode, die einen io.Reader umschließt, um Solo-Wagenrückläufe zu ersetzen. Es könnte wahrscheinlich effizienter sein, aber funktioniert mit großen Dateien besser als eine strings.Replace-basierte Lösung.

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 
} 
Verwandte Themen