在一个很大的(>1 gb)csv文件中,我有类似
"34432", "name", "0", "very long description"
但我希望
34432, "name", 0, "very long description".
我正在看,sed
但这个任务超出了我的范围。
对于如何实现这一目标,有什么建议吗?
答案1
使用 perl:
perl -ne 's/"(\d+)"/$1/g; print' file.csv > new_file.txt
所有工作都由s/"(\d+)"/$1/g
哪里完成
s/patternA/patternB/
用于patternA
替换patternB
- 然后 perl 查找一个或多个
\d+
用双引号括起来的数字。 - 括号里(
\d+
)用于捕获数字并将它们重新用作具有 perl 特殊变量的替换模式$1
。
答案2
适用于这种情况的 GNU sed 正则表达式是
sed -r 's/"([0-9]+)"/\1/g'
对于纯 sed,您需要转义分组括号和+
修饰符
sed 's/"\([0-9]\+\)"/\1/g'
您可以使用某些版本的 sed 执行替换,例如
sed -ri 's/"([0-9]+)"/\1/g' file.csv
您还可以使用 POSIX 类[[:digit:]]
代替字符范围[0-9]
答案3
您对问题的描述不够具体。我假设您只想删除第 1 和第 3 个字段周围的双引号。如果是这样,以下任何一种方法都可以:
sed
sed -r 's/^"([^"]+)"(\s*,\s*[^,]+)\s*,\s*"([^"]+)"/\1\2, \3/' file.csv
解释
启用
-r
扩展正则表达式,允许我们使用括号来捕获模式而无需对其进行转义。因此,我们在行首匹配一个引号 (^"
),后跟一个或多个非引号字符 ([^"]+
),然后是结束引号,后跟 0 个或多个空格、一个逗号,然后是 0 个或多个空格 (\s*,\s*
),然后是一段非逗号,直到下一个逗号(这定义了第二个字段)。最后,我们查找 0 个或多个空格、一个逗号,并将其替换为第一个捕获的模式 (\1
),然后是第二个 (\2
)、一个逗号、一个空格和第三个。Perl
perl -pe 's/^"([^"]+)"(\s*,\s*[^,]+)\s*,\s*"([^"]+)"/$1$2, $3/; ' file.csv
解释
意思
-p
是在应用 传递的脚本后打印每一行-e
。脚本本身与sed
上面的正则表达式基本相同。只有在这里,捕获的模式是$1
。awk
awk -F, -v OFS="," '{gsub("\"","",$1)0gsub("\"","",$3);}1;' file.csv
解释
将
-F
字段分隔符设置为,
。OFS
输出字段分隔符也设置为 ,,
以便正确打印行。gsub
进行替换,将所有替换"
为空,因为我们在第 1 个 ($1
) 和第 3 个字段 ($3
) 上运行它,因此它只会从这些字段中删除引号。1;
只是awk
“打印行”的简写。
答案4
Python 解决方案
下面的小脚本接受文件命令行参数,遍历该文件中的每一行,并使用,
分隔符将每一行拆分为项目列表。然后取消对每个条目的引号,并检查其是否为数字字符串;如果字符串是数字,则不加引号。
#!/usr/bin/env python
import sys
with open(sys.argv[1]) as fp:
for line in fp:
new_vals = []
vals = line.strip().split(',')
for val in vals:
val = val.strip().rstrip().replace('"','')
if not val.isdigit():
val = '"' + val + '"'
new_vals.append(val)
print(",".join(new_vals))
测试运行:
$ cat input.txt
"34432", "name", "0", "very long description"
"1234", "othe name" , "42", "another description"
$ ./unquote_integers.py input.txt
34432,"name",0,"very long description"
1234,"othe name",42,"another description"
补充笔记:
评论中有人问,为什么脚本在评估项目是否为数字字符串之前会删除每个项目周围的双引号。主要原因是包含双引号会使项目"123"
评估为False
,即非数字。实际上,我们需要以某种方式评估双引号内的内容。现在,有另一种方法可以通过获取每个值的列表切片来解决这个问题。但是,这并不比.replace()
从头开始使用更好。它确实使代码更短,但至少在这种情况下,脚本的简短性无关紧要 - 我们的目标是使代码正常工作,而不是对其进行代码高尔夫。
以下是使用列表切片的替代解决方案:
#!/usr/bin/env python
import sys
with open(sys.argv[1]) as fp:
for line in fp:
new_vals = []
vals = line.strip().split(',')
for val in vals:
val = val.strip().rstrip() #remove extra spaces
val = val.replace('"','') if val[1:-1].isdigit() else val
new_vals.append(val)
print(",".join(new_vals))