PHP Conference Japan 2024

sscanf

(PHP 4 >= 4.0.1, PHP 5, PHP 7, PHP 8)

sscanf根据格式解析来自字符串的输入

描述

sscanf(字符串 $string, 字符串 $format, 混合 &...$vars): 数组|整数|

函数 sscanf()printf() 的输入模拟。 sscanf() 从字符串 string 读取并根据指定的 format 进行解释。

格式字符串中的任何空格都匹配输入字符串中的任何空格。这意味着,即使格式字符串中的制表符 (\t) 也可以匹配输入字符串中的单个空格字符。

参数

string

正在解析的输入 字符串

format

string 的解释格式,在 sprintf() 的文档中进行了描述,但存在以下差异

  • 函数不考虑区域设置。
  • FgGb 不受支持。
  • D 代表十进制数。
  • i 代表具有基数检测的整数。
  • n 代表迄今为止处理的字符数。
  • s 在任何空格字符处停止读取。
  • * 代替 argnum$ 禁止此转换规范的赋值。

vars

可以选择按引用传递变量,这些变量将包含已解析的值。

返回值

如果仅将两个参数传递给此函数,则解析的值将作为数组返回。否则,如果传递了可选参数,则函数将返回已赋值值的数目。可选参数必须按引用传递。

如果 format 中期望的子字符串多于 string 中可用的子字符串,则将返回 null

示例

示例 #1 sscanf() 示例

<?php
// 获取序列号
list($serial) = sscanf("SN/2350001", "SN/%d");
// 以及制造日期
$mandate = "January 01 2000";
list(
$month, $day, $year) = sscanf($mandate, "%s %d %d");
echo
"物品 $serial 制造于:$year-" . substr($month, 0, 3) . "-$day\n";
?>

如果传递了可选参数,则函数将返回已赋值值的数目。

示例 #2 sscanf() - 使用可选参数

<?php
// 获取作者信息并生成 DocBook 条目
$auth = "24\tLewis Carroll";
$n = sscanf($auth, "%d\t%s %s", $id, $first, $last);
echo
"<author id='$id'>
<firstname>
$first</firstname>
<surname>
$last</surname>
</author>\n"
;
?>

参见

添加注释

用户贡献的注释 16 条注释

jon at fuck dot org
22 年前
此函数是获取 HTML 等效十六进制的整数 RGB 值的好方法。

list($r, $g, $b) = sscanf('00ccff', '%2x%2x%2x');
elgabos at umail dot ucsb dot edu
22 年前
在玩了一段时间之后,我发现如果你使用 %[^[]] 而不是 %s(因为 php 在使用 %s 时存在空格问题),它可以很好地工作。

对于那些不熟悉正则表达式的人,%[^^] 基本上匹配任何不是空的东西。

希望这有帮助。- Gabe
mikewillitsgmail.com
16 年前
仅供参考 - 如果你试图从包含带有扩展名的文件名的字符串中扫描。例如

<?php

$out
= sscanf('file_name.gif', 'file_%s.%s', $fpart1, $fpart2);

?>

在 $fpart1 参数中扫描的字符串结果为 'name.gif',而 $fpart2 将为 NULL。

要解决此问题,你可以简单地将 "." 替换为空格或其他“类似空格”的字符串序列。

我没有看到关于包含“.”的字符串文字的其他任何评论,所以我认为我应该提一下。我认为具有“空格分隔”内容的细微特征可能是使用争论的根源。显然,另一种方法是在这种情况下使用正则表达式,但对于新用户来说,这可能会有所帮助。

以防万一其他人像我一样花了 10 分钟的沮丧时间。这在 PHP 版本 5.2.3-1ubuntu6.3 上看到。

搜索错误报告显示另一位用户的误解:http://bugs.php.net/bug.php?id=7793
Brainiac361
19 年前
%[^^] 技巧可能看起来有效,但它无效!

发生的情况是,sscanf 将简单地匹配除开括号 [ 以外的任何字符(这比较罕见,因此它可能看起来正常工作)。
但更糟糕的是,它会期望接下来出现一个 ] 字符,并继续匹配任何内容。

现在,您可以做的是让 sscanf 查找除一个实际上从未使用过的字符以外的任何字符……一个不错的选择是换行符 "%[^\\n]",尤其是在与 fscanf 结合使用时。

您还可以做的是复制粘贴任何未使用的 ASCII 字符,例如 #001 或其他字符。
leg
16 年前
@mikewillitsgmail.com

<?php

$out
= sscanf('file_name.gif', 'file_%[^.].%s', $fpart1, $fpart2);

echo
'<pre>';
print_r($fpart1);
echo
'<hr />';
print_r($fpart2);
echo
'</pre>';

?>

输出

name
-
gif

"^." 部分避免了第一个搜索字符串过于贪婪。但无法防止 "file_test.name.gif" 这样的输入,会导致错误的结果!
codeslinger at compsalot dot com
19 年前
安全提示

虽然这是一种非常强大的技术,但请记住它很容易被欺骗。

许多成功的漏洞利用都是基于 scanf 攻击。在没有进行大量额外验证的情况下,不应将其用于不受信任的输入。
Vincent Jansen
19 年前
如果您只想过滤掉字符串中两个部分之间的信息,我使用了以下方法,它对我来说比 sscanf 函数效果更好。

<?php
function scanstr($zoekstr,$part1,$part2) {
$firstpos=strpos ($zoekstr, $part1)+strlen($part1);
$lastpos=strpos ($zoekstr, $part2);
$scanresult=substr ($zoekstr, $firstpos, $lastpos-$firstpos);
return(
$scanresult);
}
echo
scanstr ("var1=hello&var2=test&var3=more","var2=","&var3");
?>
anonymouse
18 年前
我见过一些使用括号定义看起来像正则表达式字符类的示例。在我有限的测试中,我认为它们不是真正的字符类,但它们似乎很相似。

我的任务是使用 sscanf() 解析格式为以下格式的字符串数组:

数字 空格 可能也包含空格的字符串

普通的 %s 转换命令将空格视为某种分隔符。因此,如果您事先知道会有多少个“单词”,则可以获取字符串。但是,我的输入是可变的。

这是我想出的方法:(注意使用了美元符号“字符串结束”隐藏分隔符)

sscanf($string_to_parse,'%d %[^$]s',$num,$text);

此转换命令表示“查找一个整数,然后是一个空格,然后是直到字符串结束的任何字符串”
skeltoac
18 年前
解析以通用格式存储在 Apache 访问日志中的行

<?php
$log
= array();
$n = sscanf(trim($line), '%s %s %s [%[^]]] "%s %s %[^"]" %d %s "%[^"]" "%[^"]"',
$log['ip'],
$log['client'],
$log['user'],
$log['time'],
$log['method'],
$log['uri'],
$log['prot'],
$log['code'],
$log['bytes'],
$log['ref'],
$log['agent']
);
?>
Victor
12 年前
需要注意的一点是:与 C/C++ 不同,分配给变量 %n 的值将在返回值中计算。
narainsbrain at yahoo dot com
23 年前
显然,sscanf 总是以空格分割,即使格式中未指定空格。考虑以下脚本

<?php
$str
= "This is a\tsentence with\ttabs";
$scanned = sscanf($str, "%s\t%s\t%s");
echo
join(" : ", $scanned);
?>

这会输出 "This : is : a",而不是预期的 "This is a : sentence with : tabs."。
如果您的字符串不包含空格,此行为是可以的,但如果包含空格,则最好使用 explode()。
Philo
4 年前
还应注意,当与 sscanf 一起使用时,x 和 X 会产生相同的输出(即,它们不区分大小写)。

<?php
var_dump
(sscanf("0xdead|0XDEAD", "%X|%x")); // works
joshmckenneyATgmailDOT(0{
19 年前
在电话号码函数中添加了国家代码 (1)

function formatPhone($phone) {
if (empty($phone)) return "";
if (strlen($phone) == 7)
sscanf($phone, "%3s%4s", $prefix, $exchange);
else if (strlen($phone) == 10)
sscanf($phone, "%3s%3s%4s", $area, $prefix, $exchange);
else if (strlen($phone) > 10)
if(substr($phone,0,1)=='1') {
sscanf($phone, "%1s%3s%3s%4s", $country, $area, $prefix, $exchange);
}
else{
sscanf($phone, "%3s%3s%4s%s", $area, $prefix, $exchange, $extension);
}
else
return "unknown phone format: $phone";
$out = "";
$out .= isset($country) ? $country.' ' : '';
$out .= isset($area) ? '(' . $area . ') ' : '';
$out .= $prefix . '-' . $exchange;
$out .= isset($extension) ? ' x' . $extension : '';
return $out;
}
clcollie at mindspring dot com
24 年前
实际上,如果您指定的返回值变量少于格式说明符,sscanf() _始终_ 返回一个数组。如果只存在一个格式说明符,我可能会将其更改为返回标量。
请注意,sscanf()(几乎)与其“C”对应部分具有完全相同的功能,因此您可以执行以下操作以获得预期的效果

sscanf("SN/2350001","SN/%d",&$serial)

数组返回是 PHP 的一个优点。
marcus at synchromedia dot co dot uk
21 年前
在 PHP >= 4.3.0 中,如果您使用额外的引用参数,您将收到此警告

PHP Warning: Call-time pass-by-reference has been deprecated - argument passed by value

这显然有可能导致意外后果(变量为空),并且会破坏现有代码。所以不要这样做!这些文档也需要更新以说明这一点。

语法

list($a, $b) = sscanf("hello world", "%s %s");

将按预期工作,并且似乎没有导致我注意到的任何 Apache 问题。
sbarnum.pointsystems@com
22 年前
更多关于电话的乐趣!这假设电话号码是 10 位数字,并且仅包含数字数据,但可以很容易地首先检查字符串的长度。

function formatPhone($phone) {
if (empty($phone)) return "";
sscanf($phone, "%3d%3d%4d", $area, $prefix, $exchange);
$out = @$area ? "($area) " : "";
$out .= $prefix . '-' . $exchange;
return $out;
}
To Top