多行正则表达式

17

我已经被这个问题困扰了几个小时,并尝试了许多不同的工具来完成任务,但都没有成功。如果有人能帮我解决这个问题,那就太好了。

问题如下:

我有一个非常大的CSV文件(400MB+),它的格式不正确。现在它看起来像这样:

This is a long abstract describing something. What follows is the tile for this sentence."   
,Title1  
This is another sentence that is running on one line. On the next line you can find the title.   
,Title2

你可能已经看到了,标题",Title1"和",Title2"实际上应该与前面的句子在同一行。然后它将会变成这样:

This is a long abstract describing something. What follows is the tile for this sentence.",Title1  
This is another sentence that is running on one line. On the next line you can find the title.,Title2

请注意,句子结尾可能包含引号,也可能没有。最终它们应该被替换掉。

以下是我目前想出的解决方案:

sed -n '1h;1!H;${;g;s/\."?.*,//g;p;}' out.csv > out1.csv

这应该能够匹配多行中的表达式,但不幸的是它并没有成功 :)

该表达式正在寻找句子末尾的点以及可选引号加上我试图与.*匹配的换行符。

非常感谢任何帮助。使用哪种工具都无所谓(awk、perl、sed、tr等)。


1
你有一个不特定于领域的例子吗?我很难看出你想要改变什么。 - robert
罗伯特,我改变了例子。希望这个更好 :) - herrherr
你的 CSV 文件真的只有两个字段吗?还是这只是一个简化的例子? - Daniel Haley
现在它只有这两个字段。 - herrherr
2个回答

20
sed中,多行文本并不难处理,只是它使用的命令大多数人不熟悉,并具有某些副作用,例如使用“N”将下一行附加到模式空间时,用“\n”将当前行与下一行分隔开。 无论如何,如果您匹配以逗号开头的行来决定是否删除换行符,那么处理起来就容易得多,这就是我在这里做的。
sed 'N;/\n,/s/"\? *\n//;P;D' title_csv

输入

$ cat title_csv
don't touch this line
don't touch this line either
This is a long abstract describing something. What follows is the tile for this sentence."
,Title1
seriously, don't touch this line
This is another sentence that is running on one line. On the next line you can find the title.
,Title2
also, don't touch this line

输出

$ sed 'N;/\n,/s/"\? *\n//;P;D' title_csv
don't touch this line
don't touch this line either
This is a long abstract describing something. What follows is the tile for this sentence.,Title1
seriously, don't touch this line
This is another sentence that is running on one line. On the next line you can find the title.,Title2
also, don't touch this line

谢谢。你的例子对我很有用,但是出于某种原因它不能处理我的文件。尽管模式基本相同。我已经将一个小例子上传到了我的Dropbox。也许你知道这里出了什么问题。http://dl.dropbox.com/u/84641/temp/text.csv - herrherr
1
@herrherr 好的,已经找到并修复了。在我的原始代码中,我使用了\+来匹配一个或多个空格,但我真正想要的是*来匹配零个或多个空格。 - SiegeX
1
@herrherr:我发现这个命令(sed 'N;/\n,/s/\n,/,/' text.csv)对我很有用,只要我在从DropBox下载的文件末尾添加了一个换行符。 - Jonathan Leffler
如果在",Title1"行之后有一个“不要触摸”的行,则此方法会失败。它也无法消除错误的引号。 - Dennis Williamson
@Dennis ,两个问题你都说得很好。我完全忽略了删除尾随引号的部分。至于奇怪的配对,一点 P;D 的爱就足以弥补这一点。 - SiegeX
显示剩余2条评论

14

您的工作需要做一些小修改:

sed -n '1h;1!H;${;g;s/\."\?\n,//g;p;}' inputfile

?需要进行转义,.不能匹配换行符。

以下是另一种无需使用保留空间的方法:

sed -n '${p;q};N;/\n,/{s/"\?\n//p;b};P;D' inputfile
这是一份注释版本:
sed -n '
$          # for the last input line
{
  p;             # print
  q              # and quit
};
N;         # otherwise, append the next line
/\n,/      # if it starts with a comma
{
  s/"\?\n//p;    # delete an optional comma and the newline and print the result
  b              # branch to the end to read the next line
};
P;         # it doesn't start with a comma so print it
D          # delete the first line of the pair (it's just been printed) and loop to the top
' inputfile

在我回复你的评论后才注意到你的帖子。不需要循环和特别关注最后一行。将 P;D 添加到我的脚本末尾就足够了。 - SiegeX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,