如何删除多个逗号但在csv文件中的两个值之间保留一个逗号?

时间:2016-12-26 16:22:02

标签: r bash shell csv command-line

我有一个包含数百万条记录的csv文件,如下所示

1,,,,,,,,,,a,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,456,,,,,,,,,,,,,,,,,,,,,3455,,,,,,,,,,
1,,,,,,,,,,b,,,,,,,,,,,,,,,,5,,,,,,,,,,,,,,,467,,,,,,,,,,,,,,,,,,,,,3445,,,,,,,,,,
2,,,,,,,,,,c,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,567,,,,,,,,,,,,,,,,,,,,,4656,,,,,,,,,,

我必须删除两个值之间的额外逗号,并且只保留一个。样本输入的输出应该类似于

1,a,4,456,3455
1,b,5,467,3445
2,c,6,567,4656

如何使用shell实现此功能,因为它也可以自动执行其他文件。 我需要将这些数据加载到数据库中。我们可以用R吗?

来做

5 个答案:

答案 0 :(得分:4)

编辑以解决修改过的问题。

R解决方案。

提供的原始解决方案只是处理文本。假设您的行在结构中,您可以使用:

处理多行
# Create Data
Row1 = "1,,,,,,,a,,,,,,,,,,4,,,,,,,,,456,,,,,,,,,,,3455,,,,,,,"
Row2 = "2,,,,,,,b,,,,,,,,,,5,,,,,,,,,567,,,,,,,,,,,4566,,,,,,,"
Rows = c(Row1, Row2)

CleanedRows = gsub(",+", ",", Rows)           # Compress multiple commas
CleanedRows = sub(",\\s*$", "", CleanedRows)  # Remove final comma if any
[1] "1,a,4,456,3455" "2,b,5,567,4566"

但是如果你试图从csv中读取它并压缩行,

## Create sample data
Data =read.csv(text="1,,,,,,,a,,,,,,,,,,4,,,,,,,,,456,,,,,,,,,,,3455,,,,,,,
2,,,,,,,b,,,,,,,,,,5,,,,,,,,,567,,,,,,,,,,,4566,,,,,,,",
header=FALSE)

你的代码可能会说 Data = read.csv("YourFile.csv", header=FALSE)

Data = Data[which(!is.na(Data[1,]))]
Data
  V1 V8 V18 V27  V38
1  1  a   4 456 3455
2  2  b   5 567 4566

注意:这假设非空白字段位于每行的相同位置。

答案 1 :(得分:4)

sed方法:

sed -e "s/,\+/,/g" -e "s/,$//" input_file > output_file

将多个逗号转换为单个逗号,并删除最后一行逗号。

答案 2 :(得分:2)

使用tr -s

echo 'a,,,,,,,,b,,,,,,,,,,c' | tr -s ','

输出:

a,b,c

如果输入行有逗号逗号,tr -s ','会将这些尾随逗号压缩成一个逗号,但要删除一个需要添加一些sed代码:tr -s ',' | sed 's/,$//'

速度。测试一个10,000,000行测试文件,包含OP示例中的第一行,重复。

  1. 3秒tr -s ','(但留下尾随逗号)
  2. 9秒tr -s ',' | sed 's/,$//
  3. 30秒sed -e "s/,\+/,/g" -e "s/,$//"Jean-François Fabre's answer。)

答案 3 :(得分:0)

如果你的文件确实是一个CSV文件,它可能会以几种不同的方式引用逗号,这会使基于正则表达式的CSV解析不满意。

我通常使用并推荐csvkit,它为shell提供了一套很好的CSV解析实用程序。 http://csvkit.readthedocs.io/en/latest/

的文档

使用这组命令在csvkit中回答了您的确切问题。首先,csvstat显示文件的样子:

$ csvstat -H --max tmp.csv | grep -v None 1. column1: 2 11. column11: c 27. column27: 6 42. column42: 567 63. column63: 4656

然后,既然您知道所有数据都在这些列中,那么您可以运行:

$ csvcut -c 1,11,27,42,63 tmp.csv 1,a,4,456,3455 1,b,5,467,3445 2,c,6,567,4656

获得您想要的答案。

答案 4 :(得分:0)

  

我们可以使用R吗?

如果您的输入如图所示,即您希望跳过所有行中的相同列,则可以分析第一行,然后在read.table中定义列类:

text <- "1,,,,,,,,,,a,,,,,,,,,,,,,,,,4,,,,,,,,,,,,,,,456,,,,,,,,,,,,,,,,,,,,,3455,,,,,,,,,,
         1,,,,,,,,,,b,,,,,,,,,,,,,,,,5,,,,,,,,,,,,,,,467,,,,,,,,,,,,,,,,,,,,,3445,,,,,,,,,,
         2,,,,,,,,,,c,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,567,,,,,,,,,,,,,,,,,,,,,4656,,,,,,,,,,"

tmp <- read.table(text = text, nrows = 1, sep = ",")
colClasses <- sapply(tmp, class)
colClasses[is.na(unlist(tmp))] <- "NULL" 

这里我假设第一行没有实际的NA值。如果有,你需要稍微调整一下。

read.table(text = text, sep = ",", colClasses = colClasses)
#  V1 V11 V27 V42  V63
#1  1   a   4 456 3455
#2  1   b   5 467 3445
#3  2   c   6 567 4656

显然,您指定的是file而不是text

此解决方案对于小到中等大小的数据非常有效。对于大型数据,请将第二个read.table替换为包含data {。}的fread(但无论跳过列是否存在问题,这都适用)。