正则表达式:使用dot all flag,如何为每一行添加前缀?

时间:2013-11-29 12:59:33

标签: php regex

每行前缀的正确正则表达式是什么?

说我有输入数据:

SOME OTHER DATA

TABLE
ROW
ROW
ROW
END

SOME OTHER DATA

我只对TABLE和END之间的内容感兴趣。

在php中,你可以编写一个像以下/TABLE.*?END/s这样的正则表达式,它可以匹配第一次出现在第一次出现的END。但有没有办法可以在每行前加%?结果会变成:

SOME OTHER DATA

%TABLE
%ROW
%ROW
%ROW
%END

SOME OTHER DATA

感谢任何帮助。

3 个答案:

答案 0 :(得分:3)

您可以使用单个preg_replace()执行此操作,这两种模式假设TABLE和END立即后跟换行符(或END的字符串结尾)并且前面有换行符(或者开头) TABLE的字符串:

$txt = preg_replace('~(?<=\n|^)TABLE|\G(?<!^)\R\K(?:(?=END(?:\R|$))|.+)~',
                    '%$0', $txt);

注意:此模式不检查END“标记”是否可用,因此,如果找到TABLE“标记”但没有END“标记”,则表被视为始终打开,所有连续的新行至少一个字符被视为一行直到下一个空的换行符或字符串的结尾。
但是,您可以通过添加前瞻来检查是否存在END标记来避免此行为,但是第二个模式的性能会降低而不是第一次,因为每行测试前瞻:

$pattern = '~(?:(?<=\n|^)TABLE|\G(?<!^)\R\K(?:(?=END(?:\R|$))|.+))(?=(?s).*?(?<=\nEND\r|\nEND\n|\nEND$))~';

第一个模式细节:

(?<=\n|^)TABLE      # checks if there is a newline or the start of the string
                    # before the TABLE tag
|                   # OR
\G(?<!^)            # contiguous to a precedent match but not at the start
                    # of the string
\R\K                # \R a newline, \K resets all before from the match result
(?:                 # open a non capturing group
    (?=END(?:\R|$)) # something followed by END (here the lookahead is
                    # important because, since nothing is matched, contiguous
                    # matches will be no more possible after.)
  |                 # OR
    .+              # 1 or more characters until a newline or the end of string
)                   # close the non capturing group

其他前瞻细节:

(?=             # open the lookahead
    (?s)        # the s modifier allows dot to match newlines
    .*?         # zero or more characters (lazy)
                # (note: the lazy quantifier with the lookbehind make the check
                # very slow)
    (?<=        # open a lookbehind
        \nEND\r # since a variable length lookbehind is not allowed,
      |         # you can however enumerate the different possibilities with
        \nEND\n # the different types of newlines (\r\n->Windows, \n->UNIX)
      |
        \nEND$
    )           # close the lookbehind
)               # close the lookahead

另一种方式

$arr = preg_split('/\R/', $txt);
$state = FALSE;
foreach ($arr as &$line) {
    if ($state || $line == 'TABLE') {
        $state = ($line == 'END')? FALSE : TRUE;
        $line = '%' . $line;
    }
}
$txt = implode("\n", $arr);

此代码的行为与第一个模式相同,请注意您获得了带有UNIX格式换行符的字符串。

答案 1 :(得分:1)

你走了。我创建了一个正则表达式并为您正确评论:

/(?:
 #start by finding the initial position of the table start, in order to store the match position for \G
    TABLE\n\K|
    #after we've found the table head, continue matching using this position. make sure we arent at the beginning of the string
    \G(?<!^)
)
#capture the data we're interested in
(?:
    #make sure there is no 'END' in the string
    (?!END)
    #match everything until the line ending
    .
)*
#consume the newline at the end of the string
\n/x

将结果替换为%$0

在此处查看此行动:http://regex101.com/r/rA5bV1

-

但我建议,如果您不理解我创建的正则表达式,请使用替代方法。创建一个捕获表内容的正则表达式,然后将%附加到每一行。使用以下表达式捕获内容:/TABLE\n((?:(?!END).)*)END/。我没有对此发表评论,你应该能够通过阅读其他表达的评论来弄明白。

答案 2 :(得分:0)

您应该使用2个正则表达式:

$txt = file_get_contents('input.txt');
preg_match("#(.*(?<=TABLE\n))(.*\nEND)(.*)#ms",$txt,$m);
$new = $m[1].preg_replace("#^#ms","%",$m[2]).$m[3];
print $new;

ms修饰符使得正则表达式就像整个文本是一行一样,\ n就像普通字符.一样匹配。

如果你只想在一个正则表达式中进行,你将不得不使用特殊的匹配块,如:

希望有所帮助。