如何对在 php preg_replace函数中没有匹配项的引用使用默认值


How to use default values for references that do not have a match in php preg_replace function?

我想做什么

在 php 中,像这样:

$input = '23:10';
$pattern = '/^('d?'d):('d?'d)(:('d?'d))?$/';
$rewrite = '$1h$2m$4s';
$output = preg_replace( $pattern, $rewrite, $input );
echo $output;

$input可以是有或没有秒的时间,但没有秒,它应该返回"0"作为替换字符串中$4的默认值。所以输出,现在是:

23h10ms

应该是:

23h10m0s

重要:

对于解决方案,最终函数的输入由任意$pattern$rewrite字符串组成非常重要,这些字符串将$input字符串转换为输出格式。因此,没有对该秒部分进行硬编码检查,而是实际上是在$rewrite字符串中插入引用默认值的通用方法,这些引用引用$pattern字符串中的可选部分。该函数还应该适用于以下情况(例如):

$input = '3 hours';
$pattern = '/^('d+) hours( and ('d+) minutes)?( and ('d+) seconds)?$/';
$rewrite = '$1h$3m$5s';

如本例所示,我无法控制所需的格式($pattern + $rewrite),因此它们可能会有很大差异。最重要的原因是:输入和输出也可以/将使用其他语言,因此$pattern$rewrite是从语言文件(Joomla)中获得的。

解决方案的可能方向

到目前为止,我最接近的是以下内容:

// Get a $matches array:
preg_match( $pattern, $input, $matches );
// Replace the references ($1, $2, etc.) in the rewrite string by these matches:
$output = preg_replace_callback(
  '/'$('d+)/g',
  function( $m ) {
    $i = $m[1];
    return ( isset( $matches[$i] ) ? $matches[$i] : '0' );
  },
  $rewrite
);

关于这一点的两件事:1.它还不起作用,因为$matches在回调函数中不可用。2. 使用重写字符串作为 preg_replace_callback() 的输入变得非常棘手。

更新

为了使想要的功能是动态的,我建议编写一个新类,其中所有需要的信息都是属性。在该类中,您可以使用成员方法调用preg_replace_callback,并检查是否存在所有需要的组 - 如果不存在,则应将其替换为定义的$defaultValue

class ReplaceWithDefault {
    private $pattern, $replacement, $subject, $defaultValue;
    public function __construct($pattern, $replacement, $subject, $defaultValue) {
        $this->pattern = $pattern;
        $this->replacement = $replacement;
        $this->subject = $subject;
        $this->defaultValue = $defaultValue;
    }
    public function replace() {
        return preg_replace_callback($this->pattern, 'self::callbackReplace', $this->subject);
        //alternative: return preg_replace_callback($this->pattern, array($this, 'callbackReplace'), $this->subject);
    }
    private function callbackReplace($match) {
        // fill not found groups with defaultValue
        if (preg_match_all('/'$'d{1,}/i', $this->replacement, $values)) {
            foreach ($values[0] as $value) {
                $index = substr($value, 1);//get rid of $ sign
                if (!array_key_exists($index, $match)) {
                    $match[$index] = $this->defaultValue;
                }
            }
        }
        $result = $this->replacement;
        // do the actual replacement
        krsort($match);
        foreach ($match as $key=>$value) {
            $result = str_replace('$'.$key, $value, $result);
        }
        return $result;
    }
}

你需要krsort(),所以它不会出错,例如 $15 {$1}5.

该类现在可以像这样使用

$originalQuestion = new ReplaceWithDefault('/^('d?'d):('d?'d)(:('d?'d))?$/',
    '$1h$2m$4s',
    '23:10',
    '00');
$updatedQuestion = new ReplaceWithDefault('/^('d+) hours( and ('d+) minutes)?( and ('d+) seconds)?$/',
    '$1h$3m$5s',
    '3 hours',
    '0');
print "<pre>";
var_dump($originalQuestion->replace());
var_dump($updatedQuestion->replace());

并将产生想要的结果。


原始帖子

我不确定这是否可以用常规preg_replace()来完成.但是,PHP提供了另一种可能性:preg_replace_callback()。定义函数时,可以使用更复杂的逻辑。

$input = '23:10';
$pattern = '/^('d?'d):('d?'d)(:('d?'d))?$/';
function correctFormat($hit) {
    if (isSet($hit[4]))
        $seconds = $hit[4];
    else
        $seconds = "0";
    return $hit[1]."h".$hit[2]."m".$seconds."s";
}
$output = preg_replace_callback( $pattern, "correctFormat" , $input );
echo $output;

我建议使用 Chinnu R 所示的解决方案,因为在这种情况下,正则表达式显然不是必需的,并且对常规爆炸没有任何优势。

希望对您有所帮助,

    $input = '23:10';     
    list($h, $m,$s) =  explode(":", date('H:i:s', strtotime($input)));    
    echo $h."h".$m."m".$s."s";

另一种方法;

    $input = '23:10';
    list($h, $m, $s) =  explode(":", $input);       
    if($s==""){ $s="00";};
    echo $h."h".$m."m".$s."s";