在两个标记内的任何位置匹配单词,由空格或标记分隔

时间:2014-01-09 15:28:51

标签: regex sed

使用Gnu sed,只有当两个标记出现在两个标记之间(但在它们之间的任何位置)时才能替换它,只有当该单词在左侧由起始标记分隔时才能替换或者是空格,并在右边用结尾标记或空格分隔?非常类似于在单词的两侧(标记之间)使用\b,但仅允许空格(或者如果与起始/结束标记相邻则不允许)作为分隔符。 \b标记“单词”和“非单词”字符之间的边界,并将-视为非单词字符,在这种情况下不需要。到目前为止的工作和结果,以及下面的测试用例。

[详细说明:具体来说,我正在尝试将HTML文件中的class="..."文本中的类替换为其他类。这可能是另一个例子“不要使用正则表达式来处理HTML”,但问题是如此包含的(我不在乎它是否碰巧匹配开始标记之外,例如;我不关心嵌套),感觉它应该是可能的,并且如果可能的话,将优先选择我的下一个选项,Jsoup(无论多么酷和诱人)。它感觉就像一个正则表达式和/或sed学习机会。]

起始标记是:

\(\sclass\s*=\s*"\)

(是的,我需要抓住它)

结束标记为:

"

...中间不允许"(无论是否以某种方式转义)。非常好,包含在内,不需要正确的解析。 (我将使用第二个命令来处理单引号版本。)

我想匹配像这样的东西(例如,有几个)

span\([0-9]\+\)

以下是我到目前为止的情况,将spanN更改为col-md-N(但使用\b,因此效果不正确):

s/\(\sclass\s*=\s*"\)\([^"]*\)\bspan\([0-9]\+\)\b\([^"]*\)"/\1\2col-md-\3\4"/g

它适用于此示例数据:

<div class="blah span3 arg">This has span3 in it</div>
<div class="span3">This has span3 in it</div>
<div class="span3 arg">This has span3 in it</div>

给我想要的东西:

<div class="blah col-md-3 arg">This has span3 in it</div>
<div class="col-md-3">This has span3 in it</div>
<div class="col-md-3 arg">This has span3 in it</div>

但当然它也会改变以下内容:

<div class="blah x-span3 arg">This has x-span3 in it</div>
<div class="x-span3">This has x-span3 in it</div>
<div class="x-span3 arg">This has x-span3 in it</div>
<div class="blah span3-x arg">This has span3-x in it</div>
<div class="span3-x">This has span3-x in it</div>
<div class="span3-x arg">This has span3-x in it</div>

......这是不可取的。毫无疑问,xxxspan3也应该保持不变(当然\b版本也是如此)。

是否有可能更改它们?没有重复表达三次“开头”,“中间”和“最后”的情况? (六次,如果算上单引号排列。几十次次,如果算上我需要更改的其他所有内容。)

如果答案真的是“不,你就不能”,那么,这是一个完全可以接受的答案,我会得到更大的锤子。


结语:仅供参考,这确实是“不要尝试使用正则表达式处理HTML”的另一个案例。虽然杰瑞的回答确实做到了我所需要的,但我进一步深入了解它需要更多的背景而不是正则表达式给我的。我最终使用NodeJS和cheerio DOM解析器,因为cheerio非常善于通过对标记的更改来实现最小化。

1 个答案:

答案 0 :(得分:2)

你可以试试这个正则表达式:

s/\(\sclass\s*=\s*"\)\(\([^"]*\)\( \)\)\?span\([0-9]\+\)\(\( \)\([^"]*\)\)\?"/\1\3\4col-md-\5\7\8"/g

[抱歉这是一个很长的一段时间]

我开始时(突出显示了更改):

s/\(\sclass\s*=\s*"\?\)\([^"]*\)\([" ]\)span\([0-9]\+\)\([" ]\)\([^"]*\)/\1\2\3col-md-\4\5\6/g
                   ^^           ^^^^^^^^               ^^^^^^^^         ^   

我试图捕捉"之前的span或前一空格以及span之后的数字中的任何两个空格。这也要求在替换中添加更多的反向引用并删除必须调整正则表达式的最后一个引用,但由于class=span没有资格通过,我意识到我不能只是首先引用可选或删除最后一个引用。

我因此删除了捕获组中的引号:

s/\(\sclass\s*=\s*"\)\([^"]*\)\( \)span\([0-9]\+\)\(" \)\([^"]*\)"/\1\2\3col-md-\4\5\6"/g
                              ^^^^^                ^^^^^

现在,只有报价要处理。由于我们只能有"span ...span\d+",这意味着其间的所有内容都可以选择:

s/\(\sclass\s*=\s*"\)\(\(\([^"]*\)\( \)\)\?span\([0-9]\+\)\(\(" \)\([^"]*\)\)\?"/\1\2\3col-md-\4\5\6"/g
                     ^^                ^^^^               ^^               ^^^^

唯一剩下的就是调整不同捕获组的反向引用:

s/\(\sclass\s*=\s*"\)\(\([^"]*\)\( \)\)\?span\([0-9]\+\)\(\( \)\([^"]*\)\)\?"/\1\3\4col-md-\5\7\8"/g
                                                                                ^^^^         ^^^^