如何在内容处置标头中preg_match所有三种情况



我正在尝试使用以下正则表达式解码内容处置标头(从curl(以获取文件名:

<?php
$str = 'attachment;filename="unnamed.jpg";filename*=UTF-8''unnamed.jpg'';
preg_match('/^.*?filename=(["'])([^"']+)1/m', $str, $matches);
print_r($matches);

因此,虽然文件名是否在单引号或双引号中匹配,但如果文件名周围没有引号,则会失败(这可能会发生(

$str = 'attachment;filename=unnamed.jpg;filename*=unnamed.jpg';

现在我正在使用两个正则表达式(带有 if-else(,但我只是想了解是否可以在单个正则表达式中执行?只是为了我自己学习掌握正则表达式。

我将使用分支重置功能(?|...|...|...)该功能,该功能提供了更具可读性的模式,并避免为引号创建捕获组。在分支重置组中,每个捕获组对于每个备选方案具有相同的编号:

if ( preg_match('~filename=(?|"([^"]*)"|'([^']*)'|([^;]*))~', $str, $match) )
    echo $match[1], PHP_EOL;

无论成功的替代方案是什么,捕获始终位于组 1 中。

只是为了把我的两美分放进去 - 你可以使用有条件的正则表达式

filename=(['"])?(?(1)(.+?)1|([^;]+))


细分一下,这说:
filename=   # match filename=
(['"])?     # capture " or ' into group 1, optional
(?(1)       # if group 1 was set ...
    (.+?)1 # ... then match up to 1
    |       # else
    ([^;]+) # not a semicolon
)

之后,您需要检查组 2 或 3 是否存在。
或者,使用(经常被忽视的(分支重置来获取@Casimir的答案。

regex101.com 上观看演示

一种方法是在单个正则表达式中使用交替来匹配单引号/双引号文件名或完全不带引号的文件名。 请注意,此方法的一个副作用是我们在正则表达式中引入了更多的捕获组。 因此,我们需要一些额外的逻辑来处理这个问题。

<?php
    $str = 'attachment;filename=unnamed.jpg;filename*=UTF-8''unnamed.jpg'';
    $result = preg_match('/^.*?filename=(?:(?:(["'])([^"']+)1)|([^"';]+))/m',
        $str, $matches);
    print_r($matches);
    $index = count($matches) == 3 ? 2 : 3;
    if ($result) {
        echo $matches[$index];
    }
    else {
        echo "filename not found";
    }
?>

演示

您可以将捕获组设置为可选(["'])?1?如下所示:并在非捕获组中的正则表达式末尾添加分号或字符串末尾,以检查是否有;或行尾(?:;|$)

^.*?filename=(["'])?([^"']+)1?(?:;|$)

$str = 'attachment;filename=unnamed.jpg;filename*=UTF-8''unnamed.jpg'';
preg_match('/^.*?filename=(["'])?([^"']+)1?(?:;|$)/m', $str, $matches);
print_r($matches);

输出 php

您还可以使用 K 重置报告的匹配项的起点,然后匹配,直到遇到双引号或分号[^";]+。这只会返回文件名。

^.*?filename="?K[^";]+

foreach ($strings as $string) {
    preg_match('/^.*?filename="?K[^";]+/m', $string, $matches);
    print_r($matches);
}

输出 php

最新更新