我正在尝试使用以下正则表达式解码内容处置标头(从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