如果您想使用陣列鍵作為佔位符名稱並在字串中替換對應的陣列值,不必自行發明函數,只需使用 str_replace 即可
$string = 'Hello %name!';
$data = array(
'%name' => 'John'
);
$greeting = str_replace(array_keys($data), array_values($data), $string);
(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)
vsprintf — 回傳格式化後的字串
format
格式字串由零個或多個指示詞組成:直接複製到結果的一般字元(不包括 %
)和*轉換規格*,每個轉換規格都會擷取其自身的參數。
轉換規格遵循以下原型: %[argnum$][flags][width][.precision]specifier
。
一個整數後跟一個錢字號 $
,用於指定要轉換的參數編號。
旗標 | 說明 |
---|---|
- |
在指定的欄位寬度內向左對齊;預設為向右對齊。 |
+ |
在正數前面加上加號 + ;預設情況下只有負數前面會加上負號。 |
(空格) |
使用空格填充結果。這是預設行為。 |
0 |
僅使用零來填充數字的左側。搭配 s 指定符時,也可以使用零填充右側。 |
' (字元) |
使用指定的字元 (char) 填充結果。 |
一個整數,表示此轉換應產生的最小字元數,或者使用 *
。如果使用 *
,則寬度會作為額外的整數值提供,位於指定符格式化的值之前。
一個句點 .
,後面可以選擇跟著一個整數或 *
,其含義取決於指定符。
e
、E
、f
和 F
指定符:這是要打印在小數點後的位數(預設為 6)。
g
、G
、h
和 H
指定符:這是要打印的最大有效位數。
s
指定符:它作為一個截斷點,設定字串的最大字元限制。
注意:如果指定了句點但沒有明確的精確度值,則假設為 0。如果使用
*
,則精確度會作為額外的整數值提供,位於指定符格式化的值之前。
指定符 | 說明 |
---|---|
% |
一個字面百分比字元。不需要參數。 |
b |
參數將被視為整數,並以二進位數呈現。 |
c |
參數將被視為整數,並以該 ASCII 字元呈現。 |
d |
參數將被視為整數,並以(帶符號的)十進位數呈現。 |
e |
參數將被視為科學記數法(例如 1.2e+2)。 |
E |
與 e 指定符類似,但使用大寫字母(例如 1.2E+2)。 |
f |
參數將被視為浮點數,並以浮點數呈現(考慮地區設定)。 |
F |
參數將被視為浮點數,並以浮點數呈現(不考慮地區設定)。 |
g |
一般格式。 設 P 等於精確度(如果非零),如果省略精確度則為 6,如果精確度為零則為 1。然後,如果使用樣式 E 的轉換的指數為 X 如果 P > X ≥ −4,則使用樣式 f 和精確度 P − (X + 1) 進行轉換。否則,使用樣式 e 和精確度 P − 1 進行轉換。 |
G |
與 g 指定符類似,但使用 E 和 f 。 |
h |
與 g 指定符類似,但使用 F 。從 PHP 8.0.0 開始提供。 |
H |
與 g 指定符類似,但使用 E 和 F 。從 PHP 8.0.0 開始提供。 |
o |
參數將被視為整數,並以八進位數呈現。 |
s |
參數將被視為字串並以字串呈現。 |
u |
參數將被視為整數,並以無符號十進位數呈現。 |
x |
參數將被視為整數,並以十六進位數呈現(使用小寫字母)。 |
X |
參數將被視為整數,並以十六進位數呈現(使用大寫字母)。 |
c
類型指定符會忽略填充和寬度。
嘗試將字串和寬度指定符與每個字元需要一個以上位元組的字元集組合使用可能會導致意外的結果。
變數將會被強制轉換為指定符的適用類型。
類型 | 指定符 |
---|---|
字串 | s |
整數 |
d , u , c , o , x , X , b |
浮點數 |
e , E , f , F , g , G , h , H |
值
根據 format
格式,將陣列值回傳為格式化字串。
從 PHP 8.0.0 開始,如果引數數量為零,會拋出 ValueError。在 PHP 8.0.0 之前,則會發出 E_WARNING
。
從 PHP 8.0.0 開始,如果 [width]
小於零或大於 PHP_INT_MAX
,會拋出 ValueError。在 PHP 8.0.0 之前,則會發出 E_WARNING
。
從 PHP 8.0.0 開始,如果 [precision]
小於零或大於 PHP_INT_MAX
,會拋出 ValueError。在 PHP 8.0.0 之前,則會發出 E_WARNING
。
從 PHP 8.0.0 開始,如果提供的引數少於所需數量,會拋出 ValueError。在 PHP 8.0.0 之前,會回傳 false
並發出 E_WARNING
。
版本 | 說明 |
---|---|
8.0.0 | 此函式在失敗時不再回傳 false 。 |
8.0.0 | 如果引數數量為零,會拋出 ValueError;先前此函式會發出 E_WARNING 。 |
8.0.0 | 如果 [width] 小於零或大於 PHP_INT_MAX ,會拋出 ValueError;先前此函式會發出 E_WARNING 。 |
8.0.0 | 如果 [precision] 小於零或大於 PHP_INT_MAX ,會拋出 ValueError;先前此函式會發出 E_WARNING 。 |
8.0.0 | 如果提供的引數少於所需數量,會拋出 ValueError;先前此函式會發出 E_WARNING 。 |
範例 #1 vsprintf():零填充的整數
<?php
print vsprintf("%04d-%02d-%02d", explode('-', '1988-8-1'));
?>
以上範例會輸出
1988-08-01
如果您想使用陣列鍵作為佔位符名稱並在字串中替換對應的陣列值,不必自行發明函數,只需使用 str_replace 即可
$string = 'Hello %name!';
$data = array(
'%name' => 'John'
);
$greeting = str_replace(array_keys($data), array_values($data), $string);
<?php
/**
* 類似 vsprintf,但接受 $args 鍵值而不是順序索引。
* 允許數字和符合 /[a-zA-Z0-9_-]+/ 的字串。
*
* 範例: vskprintf('y = %y$d, x = %x$1.1f', array('x' => 1, 'y' => 2))
* 結果: 'y = 2, x = 1.0'
*
* $args 也可以是物件,則其屬性會使用
* get_object_vars() 擷取。
*
* 沒有參數名稱的 '%s' 也能正常運作。vsprintf() 支援的所有功能
* 都能使用。
*
* @author Josef Kufner <jkufner(at)gmail.com>
*/
function vksprintf($str, $args)
{
if (is_object($args)) {
$args = get_object_vars($args);
}
$map = array_flip(array_keys($args));
$new_str = preg_replace_callback('/(^|[^%])%([a-zA-Z0-9_-]+)\$/',
function($m) use ($map) { return $m[1].'%'.($map[$m[2]] + 1).'$'; },
$str);
return vsprintf($new_str, $args);
}
?>
請注意,從 PHP 8.0 開始,如果出現錯誤,此函數現在會拋出 ValueError*。
$ php -r 'var_dump(vsprintf("%d", []));'
> 嚴重錯誤:未擷取的 ValueError:參數陣列必須包含 1 個項目,在命令列程式碼第 1 行給出 0 個
*ValueError 是 PHP 8.0 的新功能,因此如果您想讓您的程式碼與 PHP 7.x 相容,您應該測試參數陣列是否具有正確的長度。
<?php
/**
* 傳回一個格式化後的字串,類似 vsprintf(),但使用具名佔位符。
*
* 當佔位符在 `$args` 中找不到對應的鍵時,
* 佔位符會原樣返回,以便查看遺漏的參數。
* @param string $format
* @param array $args
* @param string $pattern
* @return string
*/
function p($format, array $args, $pattern="/\{(\w+)\}/") {
return preg_replace_callback($pattern, function ($matches) use ($args) {
return @$args[$matches[1]] ?: $matches[0];
}, $format);
}
$args = ["database"=>"people", "user"=>"staff", "pass"=>"pass123", "host"=>"localhost"];
// 使用類似 PHP 的佔位符:變數嵌入在字串 "{$database}" 中,但不包含錢字號
$format = <<<SQL
CREATE DATABASE IF NOT EXISTS {database};
GRANT ALL PRIVILEGES ON {database_name}.* TO '{user}'@'{host}';
SET PASSWORD = PASSWORD('{pass}');
SQL;
echo p($format, $args);
/*
結果:
CREATE DATABASE IF NOT EXISTS people;
GRANT ALL PRIVILEGES ON {database_name}.* TO 'staff'@'localhost';
SET PASSWORD = PASSWORD('pass123');
佔位符 `{database_name}` 在 `$args` 中沒有對應的鍵,所以會原樣返回。
*/
// 使用類似 Ruby 的佔位符
$format = <<<SQL
CREATE DATABASE IF NOT EXISTS :database;
GRANT ALL PRIVILEGES ON :database_name.* TO ':user'@':host';
SET PASSWORD = PASSWORD(':pass');
SQL;
echo p($format, $args, "/:(\w+)/");
/*
結果:
CREATE DATABASE IF NOT EXISTS people;
GRANT ALL PRIVILEGES ON :database_name.* TO 'staff'@'localhost';
SET PASSWORD = PASSWORD('pass123');
佔位符 `:database_name` 在 `$args` 中沒有對應的鍵,所以會原樣返回。
*/
這可以用於快速且簡便的國際化 (i18n)。
<?php
$GLOBALS['strings']['example'] = "總共有 %d 個人。";
// 從 lang/$lang/phrases.php 的翻譯列表中載入一個片語
function t() {
$args = func_get_args();
$nArgs = func_num_args();
$phrase = array_shift($args);
$nArgs--;
include_once("../lang/" . lang() . "/phrases.php");
if (isset($GLOBALS['strings'][$phrase])) {
return vsprintf($GLOBALS['strings'][$phrase], $args);
} else {
return '<span style="color: #ff0000">未翻譯的字串:' . $phrase . '</span>';
}
}
?>
請注意:在 4.0.4 和 4.1.0 版本之間可以使用 call_user_func_array 達到相同的功能(某種程度上)。
範例
call_user_func_array("sprintf", $arg)
$arg 的第一個元素是格式。這在 4.1.0 版本不可用的情況下拯救了我。
vnsprintf 與 vsprintf 相同,除了關聯式、帶正負號或浮點數鍵之外。
vnsprintf 支援例如 "%assocKey$05d", "%-2$'+10s" 和 "%3.2$05u",vsprintf 不支援
vnsprintf( '%2$d', $array) [第二個值] 等於 vsprintf( '%2$d', $array) [第二個值]
vnsprintf( '%+2$d', $array) [鍵 = 2] 等於 vnsprintf( '%2.0$d', $array) [鍵 = 2]
vnsprintf( '%+2$d', $array) [鍵 = 2] 與 vsprintf( '%+2$d', $array) [不支援] 不同
當您使用帶正負號或浮點數鍵時,vnsprintf 會搜尋原始陣列的帶正負號的截斷鍵
註¹:vnsprintf 不支援例如 "%someKeyf"(浮點數,鍵 = someKey)或 "%+03d"(帶正負號的十進位數,鍵 = 3),您應該分別使用 "%someKey$f" 或 "%+03$d"。
註²:"%+03d"(或 "%1$+03d")將被解釋為帶正負號的零填充十進位數
<?php
function vnsprintf( $format, array $data)
{
preg_match_all( '/ (?<!%) % ( (?: [[:alpha:]_-][[:alnum:]_-]* | ([-+])? [0-9]+ (?(2) (?:\.[0-9]+)? | \.[0-9]+ ) ) ) \$ [-+]? \'? .? -? [0-9]* (\.[0-9]+)? \w/x', $format, $match, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
$offset = 0;
$keys = array_keys($data);
foreach ( $match as &$value )
{
if ( ( $key = array_search( $value[1][0], $keys) ) !== FALSE || ( is_numeric( $value[1][0]) && ( $key = array_search( (int)$value[1][0], $keys) ) !== FALSE ) ) {
$len = strlen( $value[1][0]);
$format = substr_replace( $format, 1 + $key, $offset + $value[1][1], $len);
$offset -= $len - strlen( $key);
}
}
return vsprintf( $format, $data);
}
$examples = array(
2.8=>'positiveFloat', // key = 2 , 1st value
-3=>'negativeInteger', // key = -3 , 2nd value
'my_name'=>'someString' // key = my_name , 3rd value
);
echo vsprintf( "%%my_name\$s = '%my_name\$s'\n", $examples); // [unsupported]
echo vnsprintf( "%%my_name\$s = '%my_name\$s'\n", $examples); // output : "someString"
echo vsprintf( "%%2.5\$s = '%2.5\$s'\n", $examples); // [unsupported]
echo vnsprintf( "%%2.5\$s = '%2.5\$s'\n", $examples); // output : "positiveFloat"
echo vsprintf( "%%+2.5\$s = '%+2.5\$s'\n", $examples); // [unsupported]
echo vnsprintf( "%%+2.5\$s = '%+2.5\$s'\n", $examples); // output : "positiveFloat"
echo vsprintf( "%%-3.2\$s = '%-3.2\$s'\n", $examples); // [unsupported]
echo vnsprintf( "%%-3.2\$s = '%-3.2\$s'\n", $examples); // output : "negativeInteger"
echo vsprintf( "%%2\$s = '%2\$s'\n", $examples); // output : "negativeInteger"
echo vnsprintf( "%%2\$s = '%2\$s'\n", $examples); // output : [= vsprintf]
echo vsprintf( "%%+2\$s = '%+2\$s'\n", $examples); // [unsupported]
echo vnsprintf( "%%+2\$s = '%+2\$s'\n", $examples); // output : "positiveFloat"
echo vsprintf( "%%-3\$s = '%-3\$s'\n", $examples); // [unsupported]
echo vnsprintf( "%%-3\$s = '%-3\$s'\n", $examples); // output : "negativeInteger"
?>
使用 heredoc 與 vprintf
<?php
$string = <<<THESTRING
我喜歡 %1\$s 這個州 <br />
我選擇了:%2\$d 作為數字,<br />
我又選擇了 %2\$d 作為數字 <br />
%3\$s<br />
THESTRING;
$returnText = vprintf( $string, array('Oregon','7','我愛 Oregon') );
echo $returnText;
?>
vsprintf() 接受任何鍵值的陣列,所以在撰寫 printf 類型的函式時,不需要使用 array_shift() 的技巧。任何您需要的參數都可以很容易地從 func_get_args() 取得的陣列中移除。
<?php
function mysprintf($format) {
$args = func_get_args();
unset($args[0]); /* 移除 "$format" */
return vsprintf($format, $args);
}
/* 我在正式程式碼中使用這個技巧,如下所示: */
function logf($target, $string) {
$args = func_get_args();
unset($args[0], $args[1]);
fprintf($GLOBALS['config']['logtargets'][$target],
"[%s] %s\n", date('H:i'), wordwrap(vsprintf($string, $args), 75, '\n\r '));
}
/* 例如:
logf(DEBUG, "糟糕! %s", mysql_error());
*/
?>
據我所知,不需要 array_shift() 和其他耗費資源的陣列操作。但我可能錯了。
必須清楚說明如何在使用參數陣列時套用參數交換。有人可能會試圖使用 %0$ 來參考 $args[0]。
實際上,位置指定符永遠是陣列索引 + 1
$args[0] 是由 %1$ 參考...
$args[1] 是由 %2$ 參考...
依此類推。
同樣地,正規表示式比對的第一個子模式會在 $matches[1] 中找到,第二個在 $match[2] 中,依此類推。但是,如果 $matches 陣列被用作 vsprint() 的參數,則位置指定符是子模式 + 1
preg_match( $pattern, $subject, $matches );
vsprintf( '完整比對 = %1$s, 第一個子模式 = %2$s, 第二個子模式 = %3$s', $matches );