过滤而不依赖于 html 实体,基于开始结束匹配的一行大字符串,并将文本保留在模式之间,无论长度如何

过滤而不依赖于 html 实体,基于开始结束匹配的一行大字符串,并将文本保留在模式之间,无论长度如何

我有一个像这样的一行文件巴斯德宾但要长得多。

我的目标是仅过滤
example1: start with <aend with </a>
example2: start with PZend with的字符串部分s16
,因此在每种情况下都将文本保留在匹配之间,而不依赖于 html 实体

我已经有了一个FreeBSD依赖 html 实体的解决方法

  1. 美化为多行tidy -i -m -w 160 -ashtml -utf8 ~/file
  2. 如果不包含字符串则删除行sed -i '' '/\<\/a\>/!d' ~/file

顺便说一下,我正在尝试运行直接过滤器而不依赖 html 实体。目前我只能得到匹配的确切开头,但我不知道我正在过滤的字符串内容有多长,所以我无法精确地获取匹配的结尾,请参阅意外结果步骤来重现

重现意外结果的步骤

wget -O ~/file https://pastebin.com/raw/xbti369J
grep -E -o ".{0,0}PZ.{0,46}" ~/file

结果

因为我们要求固定长度,所以我们得到了错误的行
PZ</td><td class="s15">€ 1.20</td><td class="s16
PZ</td><td class="s15"></td><td class="s16">A</t
目标是获得线条结果模式,无论长度如何,如下所示
PZ</td><td class="s15">€ 1.20</td><td class="s16
PZ</td><td class="s15"></td><td class="s16

答案1

您想要使用 XML 解析器,例如xmllint.

使用下面的 XPath 表达式过滤掉a元素之间的文本:

xmllint --html --xpath '//a/text()' <file>

答案2

如果要选择从 aPZ到最近的每个片段s16,则需要非贪婪匹配,(扩展)正则表达式不支持这种匹配grep,但 GNU对于 Perl 样式表达式grep有erl 选项:-P

grep -P -o "PZ.*?s16" ~/file

Perl 表达式“.*?”代表使整个表达式匹配的任何字符的最短匹配。

这可能仍然不是您想要的,因为PZ比赛内部还有更多内容,但据我了解您的示例,您只需要PZ后面的那些,中间s16没有其他内容。PZ因此,让我们在第二步中删除不需要的东西:

grep -P -o "PZ.*?s16" ~/file | sed 's/.*PZ/PZ/'

答案3

有很多方法可以做到这一点。

1启用 PCRE 的 GNU grep。这里我们利用非贪婪正则表达式 *?与负前瞻一起丢弃 PZ 和 s16 之间发生的任何 PZ。

grep -Po 'PZ(?:(?!PZ).)*?s16' file

2 如果您无法访问这样的 grep 版本,您可以使用原始版本,即 Perl ,它将具有正则表达式支持。

perl -lne 'print for /PZ(?:(?!PZ).)*?s16/g' file

3 我们可以使用 sed 来做到这一点。在第一遍中,我们将 PZ 和 s16 标记为 BOL 和 EOL。然后将此修改后的输入传递给第二个 sed,该 sed 将选择以 PZ 开头、以 s16 结尾的行,并且内部不得包含 PZ。

< file \
sed 's/PZ/\n&/g;s/s16/&\n/g' |
sed '/^PZ.*s16$/!d;/..*PZ/d' |
cat

4 我们。在此仅使用一次 sed 调用。它需要 GNU sed。

sed '/\n/{
  /^PZ[^\n]*s16/!D
  s//&\n/;P;D;}
  s/PZ/\n&/g;D
' file

相关内容