首页 > 解决方案 > Golang 中的文件清理

问题描述

我有一个看起来已损坏的 csv 文件,所以我想做一些清理工作。

将文件导入 MS Access 后,我得到了以下内容,这不是必需的:

在此处输入图像描述

我想将其作为txt文件处理以进行清理,然后将其作为 csv 读取。

在文件中,我有一些字段,""有些没有,并注意到其中""包含一个,,示例:

一些数字显示为:"2,15.0而一些数字显示为22.3没有""

一些文本显示为:Manager, Supply Chain而一些文本显示为Supervisor没有""

我解决这个问题的方法是:

  1. ,如果介于两者之间,则从文件中删除""
  2. ""从文件中删除

可以说我在文件中的行是:

John, supervisor, 20.22
Mark, "Manager, SC", "3,200.0"
Joseph, "Technician, Electrical", 15.2
"Selphia, Henry", "Manager, Lab", "4,250.0"

那么清理后的文件应该是:

John, supervisor, 20.22
Mark, Manager SC, 3200.0
Joseph, Technician Electrical, 15.2
Selphia Henry, Manager Lab, 4250.0

有什么想法,支持吗?

标签: go

解决方案


感谢提供意见,set "text qualifier" to be double quote in that gui问题已解决

在此处输入图像描述

要进行适当的清理,我们不应该在清理时损坏数据!"Manager, SC"应该变成Manager, SC.

逐步执行转换,检查每一步后的结果。

John, supervisor, 20.22
Mark, "Manager, SC", "3,200.0"
Joseph, "Technician, Electrical", 15.2
"Selphia, Henry", "Manager, Lab", "4,250.0"

.csv像文件一样读取.txt文件。('"')对于每一行,用tabs替换不在引号内的逗号("\t" or 0x09).csv转换后用.tsv文件扩展名写入输入文件。检查输出.tsv文件。为了可见性,我在示例中显示了制表("\t" or 0x09)\t

John\t supervisor\t 20.22
Mark\t "Manager, SC"\t "3,200.0"
Joseph\t "Technician, Electrical"\t 15.2
"Selphia, Henry"\t "Manager, Lab"\t "4,250.0"

阅读.tsv文件。对于每个字段,修剪前导和尾随空格。如果字段的第一个和最后一个字符是引号,则('"')修剪它们。写.tsv文件。检查输出.tsv文件。

John\tsupervisor\t20.22
Mark\tManager, SC\t3,200.0
Joseph\tTechnician, Electrical\t15.2
Selphia, Henry\tManager, Lab\t4,250.0

阅读.tsv文件。对于每个字段,如果删除逗号并strconv.ParseFloat返回 err == nil 则删除逗号。写.tsv文件。检查输出.tsv文件。

John\tsupervisor\t20.22
Mark\tManager, SC\t3200.0
Joseph\tTechnician, Electrical\t15.2
Selphia, Henry\tManager, Lab\t4250.0

下面的代码可以进行所需的修剪:

package main

import (
    "fmt"
    "regexp"
    "strings"
)

// https://yourbasic.org/golang/regexp-cheat-sheet/
func main() {
    str1 := `"Selphia, Henry", "Manager, Lab", "4,250.0"` // {}

    re := regexp.MustCompile(`"\s*|,\s*"`)

    tsv := re.ReplaceAllString(str1, "\t")
    s := strings.TrimSpace(tsv)  // to remove the leading `\t` if generated
    fmt.Println(s)
}

正则表达式可以在这里检查

对于.tsv文件,使用包编码/csv:

rdr := csv.NewReader(file)
rdr.Comma = '\t'

更新

未引用字段中的前导空格很重要。使用“,”作为字段分隔符而不是“,”是错误的。例如,

John, supervisor, 20.22

在数字字段 (3,200.0) 中使用逗号需要引号来转义逗号。例如,

Mark, "Manager, SC", "3,200.0"

如果我们解决了最后两个问题,那么 csv 文件将是有效且可用的。例如,

John, supervisor, 20.22
Mark, "Manager, SC", "3,200.0"
Joseph, "Technician, Electrical", 15.2
"Selphia, Henry", "Manager, Lab", "4,250.0"

变成

John,supervisor,20.22
Mark,"Manager, SC",3200
Joseph,"Technician, Electrical",15.2
"Selphia, Henry","Manager, Lab",4250

使用 Go 包编码/csv,有一个简单的修复:

func cleanCSV(in io.Reader, out io.Writer) error {
    r := csv.NewReader(in)
    r.TrimLeadingSpace = true
    w := csv.NewWriter(out)
    for {
        rec, err := r.Read()
        if err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
        for i, fld := range rec {
            if strings.IndexByte(fld, ',') >= 0 {
                fld = strings.Replace(fld, ",", "", -1)
                if _, err := strconv.ParseFloat(fld, 64); err == nil {
                    rec[i] = fld
                }
            }
        }
        err = w.Write(rec)
        if err != nil {
            return err
        }
    }
    w.Flush()
    if err := w.Error(); err != nil {
        return err
    }
    return nil
}

完整的程序(cleancsv.go):https: //play.golang.org/p/tEc6eXCuBWD

$ go build cleancsv.go

$ ./cleancsv -i=clean.in.csv -o=clean.out.csv
cleaned: in: clean.in.csv out: clean.out.csv

$ cat clean.in.csv
John, supervisor, 20.22
Mark, "Manager, SC", "3,200.0"
Joseph, "Technician, Electrical", 15.2
"Selphia, Henry", "Manager, Lab", "4,250.0"

$ cat clean.out.csv
John,supervisor,20.22
Mark,"Manager, SC",3200.0
Joseph,"Technician, Electrical",15.2
"Selphia, Henry","Manager, Lab",4250.0

为避免在字段中使用引号转义逗号,程序 (cleanantsv.go) 输出一个tsv文件: https: //play.golang.org/p/6fOTX4_FqUM

package main

import (
    "bufio"
    "encoding/csv"
    "flag"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"
)

func cleanCSV(in io.Reader, out io.Writer) error {
    r := csv.NewReader(in)
    r.TrimLeadingSpace = true
    w := bufio.NewWriter(out)
    for {
        rec, err := r.Read()
        if err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
        for i, fld := range rec {
            if strings.IndexByte(fld, ',') >= 0 {
                fld = strings.Replace(fld, ",", "", -1)
                if _, err := strconv.ParseFloat(fld, 64); err == nil {
                    rec[i] = fld
                }
            }
        }
        _, err = w.WriteString(strings.Join(rec, "\t"))
        if err != nil {
            return err
        }
        _, err = w.WriteString("\n")
        if err != nil {
            return err
        }
    }
    err := w.Flush()
    if err != nil {
        return err
    }
    return nil
}

func cleanFile(inFile, outFile string) error {
    in, err := os.Open(inFile)
    if err != nil {
        return err
    }
    defer in.Close()
    inName := in.Name()
    out, err := os.Create(outFile)
    if err != nil {
        return err
    }
    defer out.Close()
    outName := out.Name()
    err = cleanCSV(in, out)
    if err != nil {
        return err
    }
    err = out.Close()
    if err != nil {
        return err
    }
    fmt.Println("cleaned:", "in:", inName, "out:", outName)
    return nil
}

var (
    inFile  = flag.String("i", "", "input csv file")
    outFile = flag.String("o", "", "output tsv file")
)

func main() {
    flag.Parse()
    if flag.NFlag() != 2 || *inFile == "" || *outFile == "" {
        flag.Usage()
        os.Exit(1)
    }
    err := cleanFile(*inFile, *outFile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Remove(*outFile)
        os.Exit(1)
    }
}

推荐阅读