1

我有一些这样的文本,它是用自定义的 Markdown 样式格式编写的。例如:

[Lorem ipsum] 
Dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. 

[Ut wisi] 
[Enim ad minim veniam](a), quis nostrud exerci tation ullamcorper. 
suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat. 
Vel illum dolore eu feugiat nulla facilisis at vero.
[Ros et accumsan et iusto odio dignissim](b) qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. 

[[Nam liber]](c)
Tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.

正如您所看到的,标题周围有方括号,并且有方括号后跟包含一个字母的括号,这是我试图与正则表达式匹配的内容。我正在尝试使用的正则表达式是这样的:

preg_match_all("#\[(.*?)\]\(([a-z]+)\)#is",$html,$matches)

这个 ^ one 的问题是它从 [Lorem ipsum] 匹配到 (a) 的末尾。

我也可以使用以下内容,但是我需要能够包含带有方括号的标题,因此这不能正常工作:

preg_match_all("#\[([^]]+)\]\(([a-z]+)\)#is",$html,$matches)

经过一番阅读,我怀疑我需要的是前瞻,但是我无法理解它们。非常感谢任何帮助。


澄清

我基本上希望能够用方括号/括号组合包装一些文本的任何部分,然后能够将它们与正则表达式匹配,而不会在任何地方导致冲突的现有方括号。示例文本:

[[Lorem ipsum]](a)
Dolor sit amet, [consectetuer adipiscing elit](b), sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. 

期望的匹配:

[[Lorem ipsum]](a)
[consectetuer adipiscing elit](b)

或者……更复杂

[[Lorem ipsum]
Dolor sit amet, sed diam nonummy nibh euismod](a) tincidunt ut laoreet dolore magna aliquam erat volutpat. 

期望匹配:

[[Lorem ipsum]
Dolor sit amet, sed diam nonummy nibh euismod](a)

可能吗?

4

3 回答 3

4

干得好。

preg_match_all("~
    \[(              # open outer square brackets and capturing group
    (?:              # open subpattern for optional inner square brackets
        [^[\]]*      # non-square-bracket characters
        \[           # open inner square bracket
        [^[\]]*      # non-square-bracket characters
        ]            # close inner square bracket
    )*               # end subpattern and repeat it 0 or more times
    [^[\]]*          # non-square-bracket characters
    )]               # end capturing group and outer square brackets
    (?:              # open subpattern for optional parentheses
        \((          # open parentheses and capturing group
        [a-z]+       # letters
        )\)          # close capturing group and parentheses
    )?               # end subpattern and make it optional
    ~isx",
    $input,
    $matches);

一行中的正则表达式:

"~\[((?:[^[\]]*\[[^[\]]*])*[^[\]]*)](?:\(([a-z]+)\))?~isx"

工作演示

于 2012-11-08T23:55:41.803 回答
1

m.buettner 的回答非常好。它既准确又有据可查(它得到了我的支持,值得保留选择的答案)。我真的很喜欢正则表达式以自由间距模式自我记录的事实。但是,为了完整起见(并作为另一种评论风格的演示),这里是一个等效的(但效率更高)的正则表达式解决方案:

preg_match_all('/
    # Match a "[...[...]...[...]...](...)" structure.
    \[               # Literal open square bracket.
    (                # $1: Square bracket contents.
      [^[\]]*        # {normal*} Zero or more non-[].
      (?:            # Begin {(special normal*)*}.
        \[[^[\]]*\]  # {special} Nested matching [] pair.
        [^[\]]*      # More {normal*} Zero or more non-[].
      )*             # End {(special normal*)*}.
    )                # $1: Square bracket contents.
    \]               # Literal close square bracket.
    (?:              # Optional matching parentheses.
      \(             # Literal open parentheses.
      ([A-Za-z]+)    # $2: Parentheses contents.
      \)             # Literal close parentheses.
    )?               # Optional matching parentheses.
    /x',
    $input,
    $matches);

改进(主要是外观/风格):

  • 正则表达式包含在'single quotes'而不是"double quotes". 使用 PHP,对双引号字符串进行了额外的解释,并且有更多的字符转义序列需要处理("$"特别是字符可能会导致恶作剧)。底线:使用 PHP,最好将正则表达式模式包含在单引号字符串中(即少反斜杠汤)。
  • 匹配的表达式逻辑[nested [square bracket] structure]被重新安排以实现弗里德尔“展开循环”效率技术。对于外部方括号没有嵌套方括号的情况,这会导致更少的回溯。
  • 捕获组的左括号和右括号(跨越多行)缩进到同一级别(即垂直对齐)以简化视觉匹配。
  • 捕获组编号包含在带有左括号和右括号的行的注释中。
  • s 单行修饰符被删除(不需要 - 没有点!)。
  • i 忽略大小写修饰符被删除,受影响的字符类被[a-z]更改[A-Za-z]为补偿。(在区分大小写的模式下,一些正则表达式引擎运行得更快一些。)
  • 文字"]"右方括号元字符被显式转义,即 to: "\]"。(尽管这不是必需的,但恕我直言,这是一种很好的做法)。
  • 捕获组合$2并到一行。
  • 在正则表达式的顶部添加了一个完整宽度的标题注释,描述了整个正则表达式的目的。
于 2012-11-09T04:30:18.763 回答
0

我认为你只需要稍微调整你的第一个正则表达式:

preg_match_all("#\[(.*?)\](?:\(([a-z]+)\))?#is",$html,$matches)
                          ^^^            ^^

这样,带括号的字母是可选的。

编辑:

鉴于澄清,这是一个新的正则表达式:

\[((?:[^][]|\[[^][]*?\])*?\](?:\(([a-z]+)\))?

这是一个 Rubular 演示

于 2012-11-08T23:18:31.020 回答