如何在content-disposition头中preg_match所有三种情况?

时间:2018-01-13 13:52:09

标签: php regex 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)但我只想了解是否可以在单个正则表达式中进行操作?只是为了我自己学习掌握正则表达式。

4 个答案:

答案 0 :(得分:4)

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

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

无论成功的替代方案是什么,捕获总是在第1组。

答案 1 :(得分:3)

只需将我的两分钱 - 你可以使用条件正则表达式

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的答案。

请参阅a demo on regex101.com

答案 2 :(得分:2)

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

<?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";
    }
?>

Demo

答案 3 :(得分:0)

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

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

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

Output php

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

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

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

Output php