我有以下字符串:
$string = "The man has {NUM_DOGS} dogs."
Run Code Online (Sandbox Code Playgroud)
我正在通过以下函数运行它来解析它:
function parse_text($string)
{
global $num_dogs;
$string = str_replace('{NUM_DOGS}', $num_dogs, $string);
return $string;
}
parse_text($string);
Run Code Online (Sandbox Code Playgroud)
$num_dogs预设变量在哪里.根据$num_dogs,这可能会返回以下任何字符串:
问题是,在"该男子有1只狗"的情况下,狗是多元化的,这是不希望的.我知道这可以通过不使用parse_text函数来解决,而是执行以下操作:
if($num_dogs = 1){
$string = "The man has 1 dog.";
}else{
$string = "The man has $num_dogs dogs.";
}
Run Code Online (Sandbox Code Playgroud)
但是在我的应用程序中,我解析的不仅仅是解决{NUM_DOGS}所有条件而需要很多行.
我需要一种速记方式,我可以写入初始化$string,我可以通过解析器运行,理想情况下,这不会限制我只有两个真/假的可能性.
例如,让我们
$string = 'The man has {NUM_DOGS} [{NUM_DOGS}|0=>"dogs",1=>"dog called fred",2=>"dogs called fred and harry",3=>"dogs called fred, harry and buster"].';
Run Code Online (Sandbox Code Playgroud)
是否清楚最后发生了什么?我试图使用方括号内的部分在垂直条之后开始创建数组,然后将新数组的键与{NUM_DOGS}的解析值进行比较(现在它将是$ num_dogs变量)在垂直条的左侧),并返回带有该键的数组条目的值.
如果这不是完全混淆,是否可以使用preg_*函数?
Lei*_*igh 12
您的问题的前提是您希望匹配特定模式,然后在对匹配的文本执行其他处理后替换它.
似乎是一个理想的候选人 preg_replace_callback
用于捕获匹配的括号,引号,括号等的正则表达式可能变得非常复杂,并且使用正则表达式执行所有操作实际上效率非常低.事实上,如果你需要的话,你需要编写一个合适的解析器.
对于这个问题,我将假设复杂性有限,并使用正则表达式进行两阶段解析.
首先,我可以考虑使用最简单的正则表达式来捕获花括号之间的标记.
/{([^}]+)}/
Run Code Online (Sandbox Code Playgroud)
让我们打破这一点.
{ # A literal opening brace
( # Begin capture
[^}]+ # Everything that's not a closing brace (one or more times)
) # End capture
} # Literal closing brace
Run Code Online (Sandbox Code Playgroud)
应用于字符串时preg_match_all,结果如下所示:
array (
0 => array (
0 => 'A string {TOK_ONE}',
1 => ' with {TOK_TWO|0=>"no", 1=>"one", 2=>"two"}',
),
1 => array (
0 => 'TOK_ONE',
1 => 'TOK_TWO|0=>"no", 1=>"one", 2=>"two"',
),
)
Run Code Online (Sandbox Code Playgroud)
到目前为止看起来不错
请注意,如果您的字符串中嵌套了大括号,即{TOK_TWO|0=>"hi {x} y"}此正则表达式将无效.如果这不是问题,请跳到下一部分.
可以进行顶级匹配,但我能够做到的唯一方法是通过递归.大多数正则表达式的老手会告诉你,只要你将递归添加到正则表达式,它就会停止成为一个正则表达式.
这是额外的处理复杂性开始的地方,并且对于长的复杂字符串,它很容易耗尽堆栈空间并使程序崩溃.如果您需要使用它,请仔细使用.
递归正则表达式取自我的一个其他答案并修改了一点.
`/{((?:[^{}]*|(?R))*)}/`
Run Code Online (Sandbox Code Playgroud)
坏了.
{ # literal brace
( # begin capture
(?: # don't create another capture set
[^{}]* # everything not a brace
|(?R) # OR recurse
)* # none or more times
) # end capture
} # literal brace
Run Code Online (Sandbox Code Playgroud)
而这次输出只匹配顶级大括号
array (
0 => array (
0 => '{TOK_ONE|0=>"a {nested} brace"}',
),
1 => array (
0 => 'TOK_ONE|0=>"a {nested} brace"',
),
)
Run Code Online (Sandbox Code Playgroud)
同样,除非必须,否则不要使用递归正则表达式.(如果有旧的PCRE库,您的系统可能甚至不支持它们)
如果令牌具有与之关联的选项,那么我们需要解决这个问题.根据您的问题,我建议不要让两个片段匹配,我建议按照我的示例使用令牌保留选项.{TOKEN|0=>"option"}
让我们假设$match包含一个匹配的令牌,如果我们检查一个管道|,并获取其后的所有内容的子字符串,我们将留下您的选项列表,我们再次使用正则表达式来解析它们.(别担心我最后会把所有东西都放在一起)
/(\d)+\s*=>\s*"([^"]*)",?/
坏了.
(\d)+ # Capture one or more decimal digits
\s* # Any amount of whitespace (allows you to do 0 => "")
=> # Literal pointy arrow
\s* # Any amount of whitespace
" # Literal quote
([^"]*) # Capture anything that isn't a quote
" # Literal quote
,? # Maybe followed by a comma
Run Code Online (Sandbox Code Playgroud)
和一个例子匹配
array (
0 => array (
0 => '0=>"no",',
1 => '1 => "one",',
2 => '2=>"two"',
),
1 => array (
0 => '0',
1 => '1',
2 => '2',
),
2 => array (
0 => 'no',
1 => 'one',
2 => 'two',
),
)
Run Code Online (Sandbox Code Playgroud)
如果你想在引号中使用引号,你必须为它创建自己的递归正则表达式.
总结一下,这是一个有效的例子.
一些初始化代码.
$options = array(
'WERE' => 1,
'TYPE' => 'cat',
'PLURAL' => 1,
'NAME' => 2
);
$string = 'There {WERE|0=>"was a",1=>"were"} ' .
'{TYPE}{PLURAL|1=>"s"} named bob' .
'{NAME|1=>" and bib",2=>" and alice"}';
Run Code Online (Sandbox Code Playgroud)
一切都在一起.
$string = preg_replace_callback('/{([^}]+)}/', function($match) use ($options) {
$match = $match[1];
if (false !== $pipe = strpos($match, '|')) {
$tokens = substr($match, $pipe + 1);
$match = substr($match, 0, $pipe);
} else {
$tokens = array();
}
if (isset($options[$match])) {
if ($tokens) {
preg_match_all('/(\d)+\s*=>\s*"([^"]*)",?/', $tokens, $tokens);
$tokens = array_combine($tokens[1], $tokens[2]);
return $tokens[$options[$match]];
}
return $options[$match];
}
return '';
}, $string);
Run Code Online (Sandbox Code Playgroud)
请注意错误检查是最小的,如果您选择不存在的选项,将会出现意外结果.
可能有很多简单的方法可以完成所有这些,但我只是接受了这个想法并继续使用它.
首先,它有点值得商榷,但如果你能轻易避免它,只要$num_dogs作为参数传递给函数,因为大多数人都认为全局变量是邪恶的!
接下来,为了获得"s",我通常做这样的事情:
$dogs_plural = ($num_dogs == 1) ? '' : 's';
Run Code Online (Sandbox Code Playgroud)
然后做这样的事情:
$your_string = "The man has $num_dogs dog$dogs_plural";
Run Code Online (Sandbox Code Playgroud)
它与执行if/else块基本相同,但代码行少,您只需编写一次文本.
至于另一部分,我仍然对你要做的事感到困惑,但我相信你正在寻找某种转换方式
{NUM_DOGS}|0=>"dogs",1=>"dog called fred",2=>"dogs called fred and harry",3=>"dogs called fred, harry and buster"]
Run Code Online (Sandbox Code Playgroud)
成:
switch $num_dogs {
case 0:
return 'dogs';
break;
case 1:
return 'dog called fred';
break;
case 2:
return 'dogs called fred and harry';
break;
case 3:
return 'dogs called fred, harry and buster';
break;
}
Run Code Online (Sandbox Code Playgroud)
最简单的方法是尝试使用explode()和正则表达式的组合,然后让它做我以上的事情.
在紧要关头,我做了类似于你所要求的实现,模糊地类似于下面的代码.
这远不如@ Mike的答案那么丰富,但它在过去已经成功了.
/**
* This function pluralizes words, as appropriate.
*
* It is a completely naive, example-only implementation.
* There are existing "inflector" implementations that do this
* quite well for many/most *English* words.
*/
function pluralize($count, $word)
{
if ($count === 1)
{
return $word;
}
return $word . 's';
}
/**
* Matches template patterns in the following forms:
* {NAME} - Replaces {NAME} with value from $values['NAME']
* {NAME:word} - Replaces {NAME:word} with 'word', pluralized using the pluralize() function above.
*/
function parse($template, array $values)
{
$callback = function ($matches) use ($values) {
$number = $values[$matches['name']];
if (array_key_exists('word', $matches)) {
return pluralize($number, $matches['word']);
}
return $number;
};
$pattern = '/\{(?<name>.+?)(:(?<word>.+?))?\}/i';
return preg_replace_callback($pattern, $callback, $template);
}
Run Code Online (Sandbox Code Playgroud)
以下是一些类似于您原始问题的示例......
echo parse(
'The man has {NUM_DOGS} {NUM_DOGS:dog}.' . PHP_EOL,
array('NUM_DOGS' => 2)
);
echo parse(
'The man has {NUM_DOGS} {NUM_DOGS:dog}.' . PHP_EOL,
array('NUM_DOGS' => 1)
);
Run Code Online (Sandbox Code Playgroud)
输出是:
这个男人有2只狗.
那人有一条狗.
值得一提的是,在较大的项目中,我总是放弃任何自定义滚动变形,转而支持GNU gettext,这似乎是多语言需要时最理智的方式.
| 归档时间: |
|
| 查看次数: |
1271 次 |
| 最近记录: |