我正在.csv
处理此SE数据查询看起来像这样(只有 5022 个条目):
"{
""id"": 281952,
""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
""id"": 281993,
""title"": ""Netbeans won't open in Ubuntu""
}"
(并且它^M
在 [number] 和“title”之间有行尾)。我需要它看起来像这样:
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
我很容易就用某个文本编辑器修复了这个问题,这个编辑器就不叫这个名字了,但我想写一个脚本,这样每次刷新查询时就不必再重复做这件事了,其他人也可以使用它。我用过sed
...
这一系列命令运行完美(尽管它可能效率低下;它只是一个反复试验的解决方案):
# Print the ^M and remove them, write to a new file:
cat -v QueryR* | sed 's/\^M//' > QueryNew
# remove all the other junk:
sed -i 's/{//' QueryNew
sed -i 's/}//' QueryNew
sed -i 's/""//g' QueryNew
sed -i 's/^"//' QueryNew
sed -i '/,/{N;/\n.*title:\s/{s/,\n.*title:\s/,\ /}}' QueryNew
sed -i 's/^\s\+//' QueryNew
sed -i '/^\s*$/d' QueryNew
sed -i 's/^id:\ //' QueryNew
sed -i 's/,\ /,/' QueryNew
sed -i 's/\\//g' QueryNew
那么,为什么不会这样呢?只有^M
和{}
被删除,其他的都还在那里。
#!/bin/bash
cat -v QueryR* | sed 's/\^M//' > QueryNew
sed -i '{
s/{//
s/}//
s/""//g
s/^"//
/,/{N;/\n.*title:\s/{s/,\n.*title:\s/,\ /}}
s/^\s\+//
/^\s*$/d
s/^id:\ //
s/,\ /,/
s/\\//g
}' QueryNew
我确信我的错误非常明显......
答案1
使用cat -v
将 CR 字符转换为文字^M
序列对我来说似乎从根本上来说很丑陋 - 如果您需要删除 DOS 行尾,请使用dos2unix
, tr
, 或sed 's/\r$//
'
如果你坚持使用 sed,那么我建议你打印出你做而不是试图删除所有你不想要的随机位 - 例如
$ sed -rn -e 's/\"//g' -e 's/(.*): (.*)\r/\2/p' QueryR | paste -d '' - -
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
你可以通过在值序列的每一端匹配零个或多个引号,将引号删除纳入键值提取中
$ sed -rn 's/(.*): \"*([^"]*)\"*\r/\2/p' QueryR | paste -d '' - -
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
你可以得到真的想象并模拟在结尾paste
处sed
首先连接成对的行,\r$
,然后匹配键值对乘以(g
)和非贪婪
$ sed -rn '/,\r$/ {N; s/([^:]*): \"*([^:"]*)\"*\r\n?/\2/gp}' QueryR
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
(我个人比较赞成 KISS 方法并使用第一种方法)。
FWIW,由于您的输入似乎是过度引用的 JSON,我建议安装一个合适的 JSON 解析器,例如jq
sudo apt-get install jq
然后你可以做类似的事情
$ sed -e 's/["]["]/"/g' -e 's/"{/{/' -e 's/}"/}/' QueryR | jq '.id, .title' | paste -d, - -
281952,"Flash 11.2 No Longer Supported by Google Play"
281993,"Netbeans won't open in Ubuntu"
它会删除多余的引号,然后用来jq
提取感兴趣的字段 - 请注意,这jq
似乎可以处理 DOS 样式的行尾,因此无需采取特殊步骤来删除它们。
更改为jq '.[]'
以转储所有属性值对。
灵感和基本jq
语法来源于使用 grep -o 克服换行符
答案2
感谢steeldriver和进一步的修补,我修复了这个问题。虽然不完善,但可以工作。
sed '{
s/"{//
s/}"//
s/^"//
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,/}}
s/""//g
s/^\s\+//
/^\s*$/d
s/^id:\ //
s/\\//g
}' QueryR* | tee "$1"
翻译:
s/"{//
删除"{
s/}"//
删除从行首删除一行中的}"
s/^"//
匹配项,在下一行中,将所有内容替换为删除所有剩余的双引号从行首删除空格删除空行删除其后的空格删除反斜杠(在某些标题字段中添加了“的转义字符”)运行脚本时指定一个输出文件,例如"
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}}
,\r
[whatever]title[whatever]:
,
s/""//g
s/^\s\+//
/^\s*$/d
s/^id:\ //
id:
s/\\//g
tee "$1"
./queryclean newquery.csv
答案3
虽然问题要求sed
,但人们可以解决 Python 中 sed 的问题:
from __future__ import print_function
import sys
with open(sys.argv[1]) as f:
for line in f:
if '""id""' in line:
print(line.strip().split(':')[1],end="")
if '""title""' in line:
title = " ".join(line.strip().split(':')[1:])
print(title.replace('""'," "))
此代码兼容 python2 和 python3,因此两者都可以工作
示例运行:
bash-4.3$ cat questions.txt
"{
""id"": 281952,
""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
""id"": 281993,
""title"": ""Netbeans won't open in Ubuntu""
}"
bash-4.3$ python3 parse_questions.py questions.txt
281952, Flash 11.2 No Longer Supported by Google Play
281993, Netbeans won't open in Ubuntu