preg_match搜索模式,停止在字符组合

时间:2014-08-21 13:24:10

标签: preg-match

我试图从数据库sql文件中提取整个Mysql语句

INSERT INTO `helppages` 
   (`HelpPageID`, `ShowHelpItem`, `HelpRank`, `HelpCategory`, `HelpTitle`, `HelpDescription`, `HelpLink`, `HelpText`, `CMSHelpBar`, `CMSHelpBarAdditional`) 
   VALUES (... characters (Too many to post here, but the expression below grabs all)   ... 
   );

目前,虽然我经历了很多变化,但我使用的表达方式是:

    preg_match("#INSERT INTO `$SearchingTableName` ([!%&'-/:<=>@^`\;\s\d\w\"\#\$\(\)\*\+\,\.\?\[\]\{\}\(\)\\\|©]*?)\)\;\r\n#s", $uploadedfile, $matches);
which gets all the information but I can't get it to stop at the end ");\r\n"
also $SearchingTableName = helppages.

修改

抱歉当前表达式使用了向前看

preg_match("#INSERT INTO `$SearchingTableName` ([!%&'-/:<=>@^`\;\s\d\w\"\#\$\(\)\*\+\,\.\?\[\]\{\}\(\)\\\|©]*)(?!\)\;\r\n)#s", $uploadedfile, $matches);

我还使用MSword检查;; ^ p并且插入结尾只有一个实例

1 个答案:

答案 0 :(得分:0)

要匹配此类字符串,您只能使用字符类进行操作。您需要描述字符串结构。

对于这个简单的特殊情况,您可以使用此模式:

$pattern = <<<EOD
~
# definitions
(?(DEFINE)
    (?<elt>  [^"',)]+ | '(?>[^\\']+|\\.)*' | "(?>[^\\"]+|\\.)*" )
    (?<list> \( \g<elt>? (?: \s* , \s* \g<elt> )* \) )
)

# main pattern
INSERT \s+ (?:INTO \s+)? `$SearchingTableName` \s* \g<list>? \s* VALUES \s*
\g<list> \s* (?: , \s* \g<list> \s* )* ;
~xs
EOD;

if (preg_match_all($pattern, $uploadedfile, $m))
    print_r($m[0]);

online demo

但请记住,即使对于PHP正则表达式引擎的功能,解析编程语言也不是一件容易的事,并且充满了陷阱(取决于语法)。 (但它可能。)

此处使用的正则表达式功能:

分隔符和修饰符:

此处使用的模式分隔符为~,而不是经典/。模式中没有文字~,因此它没问题。 该模式使用两个修饰符:sx

  • 默认情况下,.无法与换行符\n匹配。 s修饰符(单线模式的s)会更改此行为。使用时,.可以匹配包括换行符在内的所有字符。 (请注意,您可以使用\N检索此默认行为,无论模式如何,都不会与换行符匹配。)
  • x启用扩展模式。在此模式下,模式内的空格将被忽略。此模式允许以锐利字符#开头的内联注释。此模式对于使用空格,缩进和注释生成可读长模式非常有用。

使用命名捕获

如果您有一个长模式,并且需要多次重复使用相同的子模式,则可以重用写在捕获组内的子模式。

一个简单的例子:

您希望匹配以逗号分隔的多个项目,并使用4位数和4个字母组成,如下所示:1234abcd,5678efgh,9012ijkl,3456mnop

这样做的模式显然是^\d{4}[a-z]{4}(?:,\d{4}[a-z]{4})+$

但如果我不想两次写\d{4}[a-z]{4},我可以将它放在捕获组中并使用捕获组中子模式的别名,如下所示:^(\d{4}[a-z]{4})(?:,(?1))+$

此处(?1)是捕获组1 内的子模式的别名(不是子模式匹配的内容作为反向引用\1,而是子模式本身)那是\d{4}[a-z]{4}

PCRE,PHP使用的正则表达式引擎也支持这种语法\g<1>而不是(?1)

但是如果模式中有很多捕获组,那么记住所需捕获组的数量并不总是很方便。这就是为什么你有可能命名捕获组的原因。示例:^(?<diglet>\d{4}[a-z]{4})(?:,\g<diglet>)+$

除了使整个模式更具可读性之外,命名模式的另一个优点是为模式添加语义维度,就像通过将id属性添加到html标记一样。

定义部分

您可以使用定义部分来放置将在主模式中使用的所有子模式,而不是像上一个示例中那样直接在主模式中定义命名子模式。请注意,本节中的所有内容仅用于定义目的,并不匹配任何内容。它就像零宽度断言。

此部分的语法是:(?(DEFINE)(?<diglet>\d{4}[a-z]{4})) (您可以将几个命名的子模式放在其中。)。 precedant模式变为:(?(DEFINE)(?<diglet>\d{4}[a-z]{4}))^\g<diglet>(?:,\g<diglet>)+$

模式本身:

(?(DEFINE))之间包含的模式的第一部分包含将在主模式中稍后使用的子模式定义。

elt子模式描述了一个项目(列名或值):

[^"',)]+            # all that is not a quote a comma or a closing parenthese:
                    # in the present context this will match numbers and column names
|                   # OR
'(?>[^\\']+|\\.)*'  # string between single quotes (designed to deal with escaped quotes)
|                    
"(?>[^\\"]+|\\.)*"  # same for double quotes

list子模式描述了括号之间用逗号分隔的元素的完整列表。请注意,此子模式使用对elt子模式的引用。

主模式只需要重用子模式list