根据条件将 {token} 的部分移动到行尾
Moving parts of {token} to the end of line based on condition
如果标记位于 {} 内并匹配某个数字,我想从多行输入中移动它们。
样本输入
# (811) (1485) [2756] {29} [555] {15}
# (811) (1476) {20} {15} (1485) [196] [2441]
# (911) (619) {19} (1476) [2765] [2752] {21}
从上一行开始,如果不是 {19} 或 {20},我想将标记移动到行尾。
示例输出
# (811) (1485) [2756] [555] {15} {29}
# (811) (1476) {20} (1485) [196] [2441] {15}
# (911) (619) {19} (1476) [2765] [2752] {21}
我可以与 preg_match_all("/\{\d+\}/", $input, $matches);
进行预匹配,但之后该怎么办?
您可以将 {19}
和 {20}
每行的所有匹配项收集到一个数组中,同时过滤拆分的字符串,然后将它们重新组合在一起。
代码示例
foreach (explode("\n", $str) as $str) {
$result = array_reduce(explode(" ", $str), function($acc, $curr) {
preg_match("/{(?!19|20)\d+}/", $curr) ? $acc['move'][] = $curr : $acc['valid'][] = $curr;
return $acc;
}, ['valid' => [], 'move' => []]);
echo implode(" ", array_merge($result['valid'], array_reverse($result['move']))) . PHP_EOL;
}
输出
# (811) (1485) [2756] [555] {15} {29}
# (811) (1476) {20} (1485) [196] [2441] {15}
# (911) (619) {19} (1476) [2765] [2752] {21}
关于代码
代码首先在换行处拆分字符串,因为移动部分是每行。
然后你可以使用例如 explode 在 space 上拆分线并使用 array_reduce 检查单独的部分。
您可以使用包含 2 个数组的数组初始化数组 reduce ['valid' => [], 'move' => []]
在回调函数中,累加器 $acc
已经包含该数组,然后您可以使用 $acc['valid']
等数组键填充匹配差异
模式 {(?!19|20)\d+}
匹配 {
然后断言它后面没有直接跟随 19}
或 20}
如果是这样,它匹配 1 或大括号之间有更多数字。
要获得在“单词”之间只有单个 space 的结果,您可以合并两个数组,然后在 space.
上使用 implode
看到一个php demo.
此解决方案允许使用多个标记标点符号。在此示例中,所有以“{”或“(”开头的标记都将移至末尾:
$input = <<< STRING
# (811) (1485) [2756] {29} [555] {15}
# (811) (1476) {20} {15} (1485) [196] [2441]
# (911) (619) {19} (1476) [2765] [2752] {21}
STRING;
$excluded = [ '{19}', '{20}', '(811)' ];
$startPunctuations = array_unique(array_map(fn($exclude) => $exclude[0], $excluded));
$result = implode(
"\n",
array_map(
fn($line): string => implode(
' ',
array_map(
fn($element) => implode(' ', $element),
array_reduce(
explode(' ', $line),
fn($carry, $item) => in_array($item[0], $startPunctuations) && !in_array($item, $excluded)
? [ $carry[0], [ ...$carry[1], $item ] ]
: [ [ ...$carry[0], $item ], $carry[1] ],
[ [], [] ]
)
)
),
explode("\n", $input)
)
);
echo $result;
// # (811) [2756] [555] (1485) {29} {15}
// # (811) {20} [196] [2441] (1476) {15} (1485)
// # {19} [2765] [2752] (911) (619) (1476) {21}
您可以使用 preg_replace_callback
:
echo preg_replace_callback('~ {(?!(?:19|20)})(\d+)}(?! *$)| *$~m', function($m) {
static $rep = '';
$rep .= $m[0];
if ( !isset($m[1]) && [$ret, $rep] = [$rep, ''] ) return $ret;
}, $str);
该模式有两个分支:
- 第一个寻找每个
{token}
不是 {19}
或 {20}
并且还没有在行尾的。这个分支还包含一个 useless 捕获组(我把它放在 \d+
周围,但你可以把它放在分支的任何地方,除了 lookaheads 之外,它也可以是空的)。 =34=]
- 第二个寻找行尾(最后有尾随空格)。
如果第一个分支成功,则捕获组已定义,如果第二个分支成功,则未定义。
每次找到匹配项时,其内容都会连接到 $rep
静态变量。
但是当第二个分支成功时,返回这个累加的内容,$rep
重新初始化为空字符串。
如果标记位于 {} 内并匹配某个数字,我想从多行输入中移动它们。 样本输入
# (811) (1485) [2756] {29} [555] {15}
# (811) (1476) {20} {15} (1485) [196] [2441]
# (911) (619) {19} (1476) [2765] [2752] {21}
从上一行开始,如果不是 {19} 或 {20},我想将标记移动到行尾。
示例输出
# (811) (1485) [2756] [555] {15} {29}
# (811) (1476) {20} (1485) [196] [2441] {15}
# (911) (619) {19} (1476) [2765] [2752] {21}
我可以与 preg_match_all("/\{\d+\}/", $input, $matches);
进行预匹配,但之后该怎么办?
您可以将 {19}
和 {20}
每行的所有匹配项收集到一个数组中,同时过滤拆分的字符串,然后将它们重新组合在一起。
代码示例
foreach (explode("\n", $str) as $str) {
$result = array_reduce(explode(" ", $str), function($acc, $curr) {
preg_match("/{(?!19|20)\d+}/", $curr) ? $acc['move'][] = $curr : $acc['valid'][] = $curr;
return $acc;
}, ['valid' => [], 'move' => []]);
echo implode(" ", array_merge($result['valid'], array_reverse($result['move']))) . PHP_EOL;
}
输出
# (811) (1485) [2756] [555] {15} {29}
# (811) (1476) {20} (1485) [196] [2441] {15}
# (911) (619) {19} (1476) [2765] [2752] {21}
关于代码
代码首先在换行处拆分字符串,因为移动部分是每行。
然后你可以使用例如 explode 在 space 上拆分线并使用 array_reduce 检查单独的部分。
您可以使用包含 2 个数组的数组初始化数组 reduce ['valid' => [], 'move' => []]
在回调函数中,累加器 $acc
已经包含该数组,然后您可以使用 $acc['valid']
模式 {(?!19|20)\d+}
匹配 {
然后断言它后面没有直接跟随 19}
或 20}
如果是这样,它匹配 1 或大括号之间有更多数字。
要获得在“单词”之间只有单个 space 的结果,您可以合并两个数组,然后在 space.
上使用 implode看到一个php demo.
此解决方案允许使用多个标记标点符号。在此示例中,所有以“{”或“(”开头的标记都将移至末尾:
$input = <<< STRING
# (811) (1485) [2756] {29} [555] {15}
# (811) (1476) {20} {15} (1485) [196] [2441]
# (911) (619) {19} (1476) [2765] [2752] {21}
STRING;
$excluded = [ '{19}', '{20}', '(811)' ];
$startPunctuations = array_unique(array_map(fn($exclude) => $exclude[0], $excluded));
$result = implode(
"\n",
array_map(
fn($line): string => implode(
' ',
array_map(
fn($element) => implode(' ', $element),
array_reduce(
explode(' ', $line),
fn($carry, $item) => in_array($item[0], $startPunctuations) && !in_array($item, $excluded)
? [ $carry[0], [ ...$carry[1], $item ] ]
: [ [ ...$carry[0], $item ], $carry[1] ],
[ [], [] ]
)
)
),
explode("\n", $input)
)
);
echo $result;
// # (811) [2756] [555] (1485) {29} {15}
// # (811) {20} [196] [2441] (1476) {15} (1485)
// # {19} [2765] [2752] (911) (619) (1476) {21}
您可以使用 preg_replace_callback
:
echo preg_replace_callback('~ {(?!(?:19|20)})(\d+)}(?! *$)| *$~m', function($m) {
static $rep = '';
$rep .= $m[0];
if ( !isset($m[1]) && [$ret, $rep] = [$rep, ''] ) return $ret;
}, $str);
该模式有两个分支:
- 第一个寻找每个
{token}
不是{19}
或{20}
并且还没有在行尾的。这个分支还包含一个 useless 捕获组(我把它放在\d+
周围,但你可以把它放在分支的任何地方,除了 lookaheads 之外,它也可以是空的)。 =34=] - 第二个寻找行尾(最后有尾随空格)。
如果第一个分支成功,则捕获组已定义,如果第二个分支成功,则未定义。
每次找到匹配项时,其内容都会连接到 $rep
静态变量。
但是当第二个分支成功时,返回这个累加的内容,$rep
重新初始化为空字符串。