From ce23dfd317c2f605747e6a98c760d99aba9a0877 Mon Sep 17 00:00:00 2001 From: John Kerl Date: Mon, 16 Nov 2020 22:15:31 -0500 Subject: [PATCH] record_writer_csvlite.go --- go/src/miller/cli/mlrcli_parse.go | 25 ++--- go/src/miller/output/record_writer_csvlite.go | 91 +++++++++++++++++++ go/src/miller/output/record_writer_factory.go | 2 + 3 files changed, 106 insertions(+), 12 deletions(-) create mode 100644 go/src/miller/output/record_writer_csvlite.go diff --git a/go/src/miller/cli/mlrcli_parse.go b/go/src/miller/cli/mlrcli_parse.go index 91a044877..2b45e68c9 100644 --- a/go/src/miller/cli/mlrcli_parse.go +++ b/go/src/miller/cli/mlrcli_parse.go @@ -568,7 +568,7 @@ func parseWriterOptions(args []string, argc int, pargi *int, writerOptions *clit argi += 1 } else if args[argi] == "--ocsvlite" { - writerOptions.OutputFileFormat = "csv" + writerOptions.OutputFileFormat = "csvlite" argi += 1 } else if args[argi] == "--otsv" { @@ -576,11 +576,11 @@ func parseWriterOptions(args []string, argc int, pargi *int, writerOptions *clit writerOptions.OFS = "\t" argi += 1 - // } else if args[argi] == "--otsvlite" { - // writerOptions.OutputFileFormat = "csvlite"; - // writerOptions.OFS = "\t"; - // argi += 1; - // + } else if args[argi] == "--otsvlite" { + writerOptions.OutputFileFormat = "csvlite" + writerOptions.OFS = "\t" + argi += 1 + // } else if args[argi] == "--oasv" { // writerOptions.OutputFileFormat = "csv"; // writerOptions.OFS = ASV_FS; @@ -738,12 +738,13 @@ func parseReaderWriterOptions( writerOptions.OFS = "\t" argi += 1 - // } else if args[argi] == "--tsvlite" || args[argi] == "-t" { - // readerOptions.InputFileFormat = writerOptions.OutputFileFormat = "csvlite"; - // readerOptions.IFS = "\t"; - // writerOptions.OFS = "\t"; - // argi += 1; - // + } else if args[argi] == "--tsvlite" || args[argi] == "-t" { + readerOptions.InputFileFormat = "csvlite" + writerOptions.OutputFileFormat = "csvlite" + readerOptions.IFS = "\t" + writerOptions.OFS = "\t" + argi += 1 + // } else if args[argi] == "--asv" { // readerOptions.InputFileFormat = writerOptions.OutputFileFormat = "csv"; // readerOptions.IFS = ASV_FS; diff --git a/go/src/miller/output/record_writer_csvlite.go b/go/src/miller/output/record_writer_csvlite.go new file mode 100644 index 000000000..da59c4e98 --- /dev/null +++ b/go/src/miller/output/record_writer_csvlite.go @@ -0,0 +1,91 @@ +package output + +import ( + "bytes" + "os" + "strings" + + "miller/clitypes" + "miller/types" +) + +type RecordWriterCSVLite struct { + ofs string + ors string + + doHeaderlessOutput bool + // For reporting schema changes: we print a newline and the new header + lastJoinedHeader *string + // Only write one blank line for schema changes / blank input lines + justWroteEmptyLine bool +} + +func NewRecordWriterCSVLite(writerOptions *clitypes.TWriterOptions) *RecordWriterCSVLite { + + return &RecordWriterCSVLite{ + ofs: writerOptions.OFS, + ors: writerOptions.ORS, + + doHeaderlessOutput: writerOptions.HeaderlessCSVOutput, + lastJoinedHeader: nil, + justWroteEmptyLine: false, + } +} + +func (this *RecordWriterCSVLite) Write( + outrec *types.Mlrmap, +) { + // End of record stream: nothing special for this output format + if outrec == nil { + return + } + + if outrec.FieldCount == 0 { + if !this.justWroteEmptyLine { + os.Stdout.WriteString("\n") + } + joinedHeader := "" + this.lastJoinedHeader = &joinedHeader + this.justWroteEmptyLine = true + return + } + + needToPrintHeader := false + joinedHeader := strings.Join(outrec.GetKeys(), ",") + if this.lastJoinedHeader == nil || *this.lastJoinedHeader != joinedHeader { + if this.lastJoinedHeader != nil { + if !this.justWroteEmptyLine { + os.Stdout.WriteString("\n") + } + this.justWroteEmptyLine = true + } + this.lastJoinedHeader = &joinedHeader + needToPrintHeader = true + } + + if needToPrintHeader && !this.doHeaderlessOutput { + var buffer bytes.Buffer // faster than fmt.Print() separately + for pe := outrec.Head; pe != nil; pe = pe.Next { + buffer.WriteString(*pe.Key) + + if pe.Next != nil { + buffer.WriteString(this.ofs) + } + } + + buffer.WriteString(this.ors) + os.Stdout.WriteString(buffer.String()) + } + + var buffer bytes.Buffer // faster than fmt.Print() separately + for pe := outrec.Head; pe != nil; pe = pe.Next { + buffer.WriteString(pe.Value.String()) + if pe.Next != nil { + buffer.WriteString(this.ofs) + } + } + buffer.WriteString(this.ors) + os.Stdout.WriteString(buffer.String()) + + this.justWroteEmptyLine = false +} diff --git a/go/src/miller/output/record_writer_factory.go b/go/src/miller/output/record_writer_factory.go index 564cfcb59..bd52734ad 100644 --- a/go/src/miller/output/record_writer_factory.go +++ b/go/src/miller/output/record_writer_factory.go @@ -8,6 +8,8 @@ func Create(writerOptions *clitypes.TWriterOptions) IRecordWriter { switch writerOptions.OutputFileFormat { case "csv": return NewRecordWriterCSV(writerOptions) + case "csvlite": + return NewRecordWriterCSVLite(writerOptions) case "dkvp": return NewRecordWriterDKVP(writerOptions) case "json":