我尝试使用以下正则表达式解码内容处置标头(来自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)但我只想了解是否可以在单个正则表达式中进行操作?只是为了我自己学习掌握正则表达式。
答案 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的答案。
答案 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";
}
?>
答案 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);
您还可以使用\K
重置报告的匹配的起点,然后匹配,直到遇到双引号或分号[^";]+
。这只会返回文件名。
foreach ($strings as $string) {
preg_match('/^.*?filename="?\K[^";]+/m', $string, $matches);
print_r($matches);
}