Php 正则表达式解析define()内容,可能吗?
我对regex很陌生,这对我来说太高级了。所以我在问这里的专家 问题 我想从php define()中检索常量/值 基本上,我希望正则表达式能够返回常量的名称,以及上面一行中常量的值。只有文本和值。这可能吗Php 正则表达式解析define()内容,可能吗?,php,regex,Php,Regex,我对regex很陌生,这对我来说太高级了。所以我在问这里的专家 问题 我想从php define()中检索常量/值 基本上,我希望正则表达式能够返回常量的名称,以及上面一行中常量的值。只有文本和值。这可能吗 为什么我需要它?我正在处理语言文件,我想得到所有的对(名称、值),并将它们放入数组中。我用str_replace()和trim()等工具成功地实现了这一点。。但这条路很长,我相信使用单行正则表达式可以使它变得更容易 注意:该值也可能包含转义单引号。例如: DEFINE('TEXT', 'J\
为什么我需要它?我正在处理语言文件,我想得到所有的对(名称、值),并将它们放入数组中。我用str_replace()和trim()等工具成功地实现了这一点。。但这条路很长,我相信使用单行正则表达式可以使它变得更容易 注意:该值也可能包含转义单引号。例如:
DEFINE('TEXT', 'J\'ai');
我希望我没有要求太复杂的事情
关于您可能不需要过分考虑正则表达式的复杂性-类似这样的东西可能就足够了
/DEFINE\('(.*?)',\s*'(.*)'\);/
下面是一个PHP示例,展示了如何使用它
$lines=file("myconstants.php");
foreach($lines as $line) {
$matches=array();
if (preg_match('/DEFINE\(\'(.*?)\',\s*\'(.*)\'\);/i', $line, $matches)) {
$name=$matches[1];
$value=$matches[2];
echo "$name = $value\n";
}
}
这是可能的,但我宁愿使用。但是要确保所有的翻译都有共同点(比如所有以T开头的翻译),这样就可以将它们与其他常量区分开来。尝试使用此正则表达式查找
定义调用:
/\bdefine\(\s*("(?:[^"\\]+|\\(?:\\\\)*.)*"|'(?:[^'\\]+|\\(?:\\\\)*.)*')\s*,\s*("(?:[^"\\]+|\\(?:\\\\)*.)*"|'(?:[^'\\]+|\\(?:\\\\)*.)*')\s*\);/is
因此:
对于任何一种基于语法的解析,正则表达式通常都是一个糟糕的解决方案。即使是smple语法(比如算术)也有嵌套,正则表达式就是在嵌套(尤其是)的基础上崩溃的
幸运的是,PHP为您提供了一个更好得多的解决方案,它允许您通过访问PHP解释器使用的相同词法分析器。给它一个PHP代码的字符流,它将把它解析成记号(“lexemes”),你可以用一个非常简单的方法对它进行一些简单的解析
运行这个程序(它作为test.php运行,所以它会自己尝试)。该文件故意格式化不好,因此您可以看到它可以轻松地处理该文件
<?
define('CONST1', 'value' );
define (CONST2, 'value2');
define( 'CONST3', time());
define('define', 'define');
define("test", VALUE4);
define('const5', //
'weird declaration'
) ;
define('CONST7', 3.14);
define ( /* comment */ 'foo', 'bar');
$defn = 'blah';
define($defn, 'foo');
define( 'CONST4', define('CONST5', 6));
header('Content-Type: text/plain');
$defines = array();
$state = 0;
$key = '';
$value = '';
$file = file_get_contents('test.php');
$tokens = token_get_all($file);
$token = reset($tokens);
while ($token) {
// dump($state, $token);
if (is_array($token)) {
if ($token[0] == T_WHITESPACE || $token[0] == T_COMMENT || $token[0] == T_DOC_COMMENT) {
// do nothing
} else if ($token[0] == T_STRING && strtolower($token[1]) == 'define') {
$state = 1;
} else if ($state == 2 && is_constant($token[0])) {
$key = $token[1];
$state = 3;
} else if ($state == 4 && is_constant($token[0])) {
$value = $token[1];
$state = 5;
}
} else {
$symbol = trim($token);
if ($symbol == '(' && $state == 1) {
$state = 2;
} else if ($symbol == ',' && $state == 3) {
$state = 4;
} else if ($symbol == ')' && $state == 5) {
$defines[strip($key)] = strip($value);
$state = 0;
}
}
$token = next($tokens);
}
foreach ($defines as $k => $v) {
echo "'$k' => '$v'\n";
}
function is_constant($token) {
return $token == T_CONSTANT_ENCAPSED_STRING || $token == T_STRING ||
$token == T_LNUMBER || $token == T_DNUMBER;
}
function dump($state, $token) {
if (is_array($token)) {
echo "$state: " . token_name($token[0]) . " [$token[1]] on line $token[2]\n";
} else {
echo "$state: Symbol '$token'\n";
}
}
function strip($value) {
return preg_replace('!^([\'"])(.*)\1$!', '$2', $value);
}
?>
这基本上是一个查找模式的有限状态机:
function name ('define')
open parenthesis
constant
comma
constant
close parenthesis
在PHP源文件的词法流中,并将这两个常量视为(名称、值)对。在此过程中,它处理嵌套的define()语句(根据结果),忽略空格和注释,并跨多行工作
注意:我已经仔细考虑过了,让它忽略函数和变量是常量名称或值的情况,但您可以根据需要扩展它
还值得指出的是,PHP在字符串方面非常宽容。可以使用单引号、双引号或(在某些情况下)完全不使用引号来声明它们。这可能(正如Gumbo所指出的)是一个对常数的模糊引用,而您无法知道它是哪一个(无论如何也无法保证),这给您带来了以下好处:
忽略该样式的字符串(T_字符串)李>
查看是否已使用该名称声明了常量,并替换其值。但是,您无法知道调用了哪些其他文件,也无法处理任何有条件创建的定义,因此您无法确定任何内容是否为常量,也无法确定它具有什么值;或
您可以接受这些可能是常量(这不太可能),并将它们视为字符串
就我个人而言,我会选择(1)然后(3)。不是所有文本问题都应该用regexp解决,所以我建议您说明您想要实现什么,而不是如何实现
因此,与其使用php的解析器,或者使用完全不可调试的正则表达式,不如编写一个简单的解析器
<?php
$str = "define('nam\\'e', 'va\\\\\\'lue');\ndefine('na\\\\me2', 'value\\'2');\nDEFINE('a', 'b');";
function getDefined($str) {
$lines = array();
preg_match_all('#^define[(][ ]*(.*?)[ ]*[)];$#mi', $str, $lines);
$res = array();
foreach ($lines[1] as $cnt) {
$p = 0;
$key = parseString($cnt, $p);
// Skip comma
$p++;
// Skip space
while ($cnt{$p} == " ") {
$p++;
}
$value = parseString($cnt, $p);
$res[$key] = $value;
}
return $res;
}
function parseString($s, &$p) {
$quotechar = $s[$p];
if (! in_array($quotechar, array("'", '"'))) {
throw new Exception("Invalid quote character '" . $quotechar . "', input is " . var_export($s, true) . " @ " . $p);
}
$len = strlen($s);
$quoted = false;
$res = "";
for ($p++;$p < $len;$p++) {
if ($quoted) {
$quoted = false;
$res .= $s{$p};
} else {
if ($s{$p} == "\\") {
$quoted = true;
continue;
}
if ($s{$p} == $quotechar) {
$p++;
return $res;
}
$res .= $s{$p};
}
}
throw new Exception("Premature end of line");
}
var_dump(getDefined($str));
那个么非文字值(如define('NOW',time())
)呢?我正在处理的语言文件只包含两边单引号中的文本。常量名称和值。所以名称和值始终是固定字符串?但这样做可能需要编辑太多行?他们没有共同之处。。所以我想我可以用一个正则表达式这就是为什么我叫保罗。这只检查模式定义('text','value'),对吗?--我的意思是如果我想收集下一个文本和值。。我该怎么做?工作得很有魅力!~救了我很多!犯错误这在任何琐碎的变化上都是失败的,比如定义(“废话”,“foo”)。此外,在除现有间距以外的任何间距上,定义跨越多条直线、heredocs和foth。这甚至在定义('const','foo')时失败;克莱特斯,我喜欢这个,因为它很短,非常适合我的需要。假设翻译总是在单引号中,并且总是字符串。。无论如何,要使它在定义('constant','value')时不会失败?我的意思是如果空间被遗漏了?如果常数2已经是一个常数呢<代码>定义('foo','bar');定义(foo,'baz')代码>=>foo='bar',bar='baz'常量2是一个T_字符串常量。通过额外的检查,您可以检查是否获得T_字符串常量,然后对其使用is_defined(),获取值,或者,如果未定义,则将其视为字符串(与PHP一样)。“CONST2是T_字符串常量。”–哦,我忘了:它是PHP.)我印象深刻,它返回的输出就像书上说的那样。目前,这两种解决方案都很好,并且都在发挥作用。谢谢你们两位
<?
define('CONST1', 'value' );
define (CONST2, 'value2');
define( 'CONST3', time());
define('define', 'define');
define("test", VALUE4);
define('const5', //
'weird declaration'
) ;
define('CONST7', 3.14);
define ( /* comment */ 'foo', 'bar');
$defn = 'blah';
define($defn, 'foo');
define( 'CONST4', define('CONST5', 6));
header('Content-Type: text/plain');
$defines = array();
$state = 0;
$key = '';
$value = '';
$file = file_get_contents('test.php');
$tokens = token_get_all($file);
$token = reset($tokens);
while ($token) {
// dump($state, $token);
if (is_array($token)) {
if ($token[0] == T_WHITESPACE || $token[0] == T_COMMENT || $token[0] == T_DOC_COMMENT) {
// do nothing
} else if ($token[0] == T_STRING && strtolower($token[1]) == 'define') {
$state = 1;
} else if ($state == 2 && is_constant($token[0])) {
$key = $token[1];
$state = 3;
} else if ($state == 4 && is_constant($token[0])) {
$value = $token[1];
$state = 5;
}
} else {
$symbol = trim($token);
if ($symbol == '(' && $state == 1) {
$state = 2;
} else if ($symbol == ',' && $state == 3) {
$state = 4;
} else if ($symbol == ')' && $state == 5) {
$defines[strip($key)] = strip($value);
$state = 0;
}
}
$token = next($tokens);
}
foreach ($defines as $k => $v) {
echo "'$k' => '$v'\n";
}
function is_constant($token) {
return $token == T_CONSTANT_ENCAPSED_STRING || $token == T_STRING ||
$token == T_LNUMBER || $token == T_DNUMBER;
}
function dump($state, $token) {
if (is_array($token)) {
echo "$state: " . token_name($token[0]) . " [$token[1]] on line $token[2]\n";
} else {
echo "$state: Symbol '$token'\n";
}
}
function strip($value) {
return preg_replace('!^([\'"])(.*)\1$!', '$2', $value);
}
?>
'CONST1' => 'value'
'CONST2' => 'value2'
'CONST3' => 'time'
'define' => 'define'
'test' => 'VALUE4'
'const5' => 'weird declaration'
'CONST7' => '3.14'
'foo' => 'bar'
'CONST5' => '6'
function name ('define')
open parenthesis
constant
comma
constant
close parenthesis
<?php
$str = "define('nam\\'e', 'va\\\\\\'lue');\ndefine('na\\\\me2', 'value\\'2');\nDEFINE('a', 'b');";
function getDefined($str) {
$lines = array();
preg_match_all('#^define[(][ ]*(.*?)[ ]*[)];$#mi', $str, $lines);
$res = array();
foreach ($lines[1] as $cnt) {
$p = 0;
$key = parseString($cnt, $p);
// Skip comma
$p++;
// Skip space
while ($cnt{$p} == " ") {
$p++;
}
$value = parseString($cnt, $p);
$res[$key] = $value;
}
return $res;
}
function parseString($s, &$p) {
$quotechar = $s[$p];
if (! in_array($quotechar, array("'", '"'))) {
throw new Exception("Invalid quote character '" . $quotechar . "', input is " . var_export($s, true) . " @ " . $p);
}
$len = strlen($s);
$quoted = false;
$res = "";
for ($p++;$p < $len;$p++) {
if ($quoted) {
$quoted = false;
$res .= $s{$p};
} else {
if ($s{$p} == "\\") {
$quoted = true;
continue;
}
if ($s{$p} == $quotechar) {
$p++;
return $res;
}
$res .= $s{$p};
}
}
throw new Exception("Premature end of line");
}
var_dump(getDefined($str));
array(3) {
["nam'e"]=>
string(7) "va\'lue"
["na\me2"]=>
string(7) "value'2"
["a"]=>
string(1) "b"
}