這個函式是從 HTML 等效十六進位制值取得整數 RGB 值的好方法。
list($r, $g, $b) = sscanf('00ccff', '%2x%2x%2x');
(PHP 4 >= 4.0.1, PHP 5, PHP 7, PHP 8)
sscanf — 根據格式從字串解析輸入
函式 sscanf() 是 printf() 的輸入版本。sscanf() 從字串 string
讀取資料,並根據指定的 format
進行解譯。
格式字串中的任何空格都符合輸入字串中的任何空格。這表示即使格式字串中的定位字元 (\t
) 也能符合輸入字串中的單個空格字元。
如果只傳遞兩個參數給此函式,解析後的值將會以陣列形式返回。否則,如果傳遞了選用參數,函式將會返回賦值的數量。選用參數必須以傳址方式傳遞。
如果在 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";
?>
這個函式是從 HTML 等效十六進位制值取得整數 RGB 值的好方法。
list($r, $g, $b) = sscanf('00ccff', '%2x%2x%2x');
在玩了一段時間之後,我發現如果使用 %[^[]] 而不是 %s(因為 PHP 在使用 %s 時會出現空格問題),它就能正常運作。
對於不熟悉正規表示式的人來說,%[^[]] 基本上會匹配任何非空字元。
希望這有幫助。 - Gabe
僅供參考 - 如果您嘗試從包含帶副檔名的檔案名的字串進行掃描。例如
<?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
`%[^[]` 的技巧看似有效,但實際上並非如此!
`sscanf` 只會匹配任何不是左方括號的字元(這種情況相當少見,所以它可能看起來有效)。
但更糟的是,它會接著預期一個 `]` 字元,然後繼續匹配任何字元。
你可以做的是讓 `sscanf` 尋找任何不是真正從未使用的字元的字元……一個好的選擇是換行符號 `%[^\\n]`,尤其是在與 `fscanf` 結合使用時。
你也可以複製並貼上任何未使用的 ASCII 字元,例如 #001 或其他類似字元。
@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" 時出現錯誤結果!
安全性注意事項
雖然這是一個非常強大的技巧,但請記住,它很容易被欺騙。
許多成功的漏洞利用都是基於 scanf 攻擊。不應在未經大量額外驗證的情況下將其用於不受信任的輸入。
如果您只想篩選出字串兩個部分之間的資訊,我使用了以下方法,它對我來說比 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");
?>
我看過幾個例子,人們使用方括號來定義看起來像正規表達式字元類別的東西。在我有限的測試中,我不認為它們是真正的字元類別,但它們看起來很相似。
我的任務是使用 sscanf() 解析格式如下的字串陣列:
數字 空格 可能也包含空格的字串
一般的 %s 轉換指令會將空格視為某種分隔符號。因此,如果您事先知道會有多少個「單詞」,就可以取得字串。但是,我的輸入是可變的。
以下是我想出的方法:(請注意使用錢字號 '$' 作為隱藏的字串結尾分隔符號)
sscanf($string_to_parse,'%d %[^$]s',$num,$text);
這個轉換指令表示「尋找一個整數,然後是一個空格,然後是到字串結尾的任何字串」
解析通用格式的 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']
);
?>
顯然地,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() 函式。
還需要注意的是,當與 sscanf 一起使用時,x 和 X 會產生相同的輸出(即它們不區分大小寫)。
<?php
var_dump(sscanf("0xdead|0XDEAD", "%X|%x")); // 正常運作
在電話號碼函式中加入了國家代碼 (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 "未知的電話號碼格式: $phone";
$out = "";
$out .= isset($country) ? $country.' ' : '';
$out .= isset($area) ? '(' . $area . ') ' : '';
$out .= $prefix . '-' . $exchange;
$out .= isset($extension) ? ' x' . $extension : '';
return $out;
}
實際上,如果你指定的回傳變數少於格式指定符,sscanf() 總是會回傳一個陣列。我可能會將其更改為如果只有一個格式指定符,則回傳一個純量值。
請注意,sscanf()(幾乎)與其「C 語言」版本的功能完全相同,因此你可以執行以下操作來獲得預期的效果
sscanf("SN/2350001","SN/%d",&$serial)
陣列回傳是 PHP 的一個優點。
在 PHP >= 4.3.0 中,如果你使用額外的參考參數,你會收到以下警告
PHP 警告:呼叫時傳遞參考已被棄用 - 參數以值傳遞
這顯然有可能導致意想不到的後果(變數保持空白),並且會破壞現有的程式碼。所以不要這樣做!這些文件也需要更新以說明這一點。
語法
list($a, $b) = sscanf("hello world", "%s %s");
將會按預期工作,並且在我注意到的情況下似乎不會對 Apache 造成任何問題。
電話號碼的更多樂趣!這裡假設電話號碼是 10 位數,且僅包含數字數據,但可以很容易地先檢查字串的長度。
function formatPhone($phone) {
if (empty($phone)) return "";
sscanf($phone, "%3d%3d%4d", $area, $prefix, $exchange);
$out = @$area ? "($area) " : "";
$out .= $prefix . '-' . $exchange;
return $out;
}