使用php regexp重新排序字符串的行

时间:2018-07-27 09:15:07

标签: php regex algorithm

我需要使用php regexp重新排序字符串中的行。但是我不知道如何告诉PHP不要两次更改同一行。让我解释一下。

输入字符串为:

$comment = "
some text

{Varinat #3 smth}
{Varinat #4 smth else}
{Varinat #1 smth else 1}
some another text
{Varinat #2 smth else 2}
{Varinat #5 smth else 5}
";

我需要订购变体:

$comment = "
some text

{Varinat #1 smth else 1}
{Varinat #2 smth else 2}
{Varinat #3 smth}
some another text
{Varinat #4 smth else}
{Varinat #5 smth else 5}    
";

我有代码:

$variants = [
    3 => 1,
    4 => 2,
    1 => 3,
    2 => 4,
    5 => 5,
];

$replacements = [];
foreach ($variants as $key => $variant) {
    $replacements['/{Varinat #'.$variant.'\ /is'] = '{Varinat #'.$key . ' ';
}


$comment = preg_replace(array_keys($replacements), array_values($replacements), $comment);

echo $comment;

但是它确实改变了:

some text

{Varinat #1 smth}
{Varinat #2 smth else}
{Varinat #1 smth else 1}
some another text
{Varinat #2 smth else 2}
{Varinat #5 smth else 5}

如您所见,第1和2行加倍。 发生这种情况是因为php确实发生了变化:3-> 1,然后1-> 3。

我唯一的解决方法是:将行更改为

3 => 1*,
4 => 2*,
1 => 3*,
2 => 4*,
5 => 5*,

然后删除*

还有更优雅的解决方案吗?

2 个答案:

答案 0 :(得分:3)

为什么不构建一种算法来实际执行您想做的事情,那就是对{Varinat行进行排序?

$lines = explode("\n",$comment); // assuming $comment is your input here
$numbered_lines = array_map(null,$lines,range(1,count($lines)));
usort($numbered_lines,function($a,$b) {
    if( preg_match('(^\{Varinat #(\d+))', $a[0], $match_a)
     && preg_match('(^\{Varinat #(\d+))', $b[0], $match_b)) {
        return $match_a[1] - $match_b[1]; // sort based on variant number
    }
    return $a[1] - $b[1]; // sort based on line number
});
$sorted_lines = array_column($numbered_lines,0);
$result = implode("\n",$sorted_lines);

由于某些原因,以上代码在PHP 7中不起作用。这是一个替代方法。

$lines = explode("\n",$comment);
$processed = array_map(function($line) {
    if( preg_match('(^\{Varinat #(\d+))', $line, $match)) {
        return [$line,$match[1]];
    }
    return [$line,null];
}, $lines);
$variant = array_filter($processed,function($data) {return $data[1];});
usort($variant,function($a,$b) {return $a[1] - $b[1];});
$sorted = array_map(function($data) use (&$variant) {
    if( $data[1]) return array_shift($variant)[0];
    else return $data[0];
},$processed);
$result = implode("\n",$sorted);

这可以通过首先用“变体”数字标记每行(如果有)来实现。然后,它将列表筛选为仅这些行并对其进行排序。最后,它再次遍历所有行,或者保持原样(如果不是变体),或者将其替换为下一个排序的变体行。

> 3v4l上的演示

答案 1 :(得分:0)

更复杂,但是算法看起来像这样:

  1. 替换行以具有唯一占位符的"{Variant #"开头,即"%line" . $i,其中$i是自动递增的,并将删除的部分存储在临时数组中。可以通过preg_replace_callback()方法完成
  2. 排序临时数组
  3. preg_replace_callback()再次替换临时数组中具有连续项的占位符。