如果你想使用数组键作为占位符名称,并在字符串中替换相应的数组值,而不是发明自己的函数,直接使用 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
.
一个整数,后面跟着美元符号 $
,用于指定转换中要处理的哪个数字参数。
Flag | 描述 |
---|---|
- |
在给定的字段宽度内左对齐;右对齐是默认值 |
+ |
在正数前面加一个加号 + ;默认情况下,只有负数前面加一个负号。 |
(空格) |
用空格填充结果。这是默认值。 |
0 |
仅用零左填充数字。对于 s 说明符,也可以用零右填充。 |
' (字符) |
用字符 (char) 填充结果。 |
要么是一个整数,表示此转换应产生的字符数(最小值),要么是 *
。如果使用 *
,则宽度作为在由说明符格式化的一个整数之前提供的另一个整数的值提供。
一个句点 .
,后面可以选择跟着一个整数或 *
,其含义取决于说明符
e
、E
、f
和 F
说明符:这是小数点后要打印的位数(默认情况下,为 6)。
g
、G
、h
和 H
说明符:这是要打印的有效位数的最大值。
s
说明符:它充当截止点,为字符串设置最大字符限制。
注意: 如果指定了句点但不指定精度的明确值,则假定为 0。如果使用
*
,则精度作为在由说明符格式化的一个整数之前提供的另一个整数的值提供。
Specifier | 描述 |
---|---|
% |
一个文字百分号字符。不需要参数。 |
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
类型说明符忽略填充和宽度。
尝试将字符串和宽度说明符组合使用,以及需要一个字节以上的字符集,可能会导致意外的结果。
变量将被强制转换为适合说明符的类型
类型 | Specifiers |
---|---|
string | s |
int |
d 、u 、c 、o 、x 、X 、b |
float |
e 、E 、f 、F 、g 、G 、h 、H |
values
根据 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 个项目,给出 0 个项目,在命令行代码:1
*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` 中没有匹配的键,因此按原样返回。
*/
这可用于快速粗略的国际化
<?php
$GLOBALS['strings']['example'] = "There are %d people.";
// 从 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">Untranslated string: ' . $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) [第 2 个值] 等于 vsprintf( '%2$d', $array) [第 2 个值]
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','我爱俄勒冈') );
echo $returnText;
?>
vsprintf() 接受带有任意键的数组,因此在编写 printf 类型函数时,array_shift() 技术是不必要的。您需要的任何参数都可以从您使用 func_get_args() 获取的数组中轻松 unset。
<?php
function mysprintf($format) {
$args = func_get_args();
unset($args[0]); /* get rid of "$format" */
return vsprintf($format, $args);
}
/* I use this technique in production code as follows: */
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 '));
}
/* e.g.:
logf(DEBUG, "Oops! %s", mysql_error());
*/
?>
据我所知,array_shift() 和其他代价高昂的数组操作是不需要的。我可能错了。
需要清楚地说明在使用参数数组时如何应用参数交换。有人可能会倾向于使用 %0$ 来引用 $args[0]。
实际上,位置说明符始终是数组索引+1
$args[0] 由 %1$ 引用...
$args[1] 由 %2$ 引用...
等等。
类似地,RegEx 匹配的第一个子模式将位于 $matches[1] 中,第二个位于 $match[2] 中,依此类推。但是,如果 $matches 数组用作 vsprint() 的参数,则位置说明符是子模式+1
preg_match( $pattern, $subject, $matches );
vsprintf( 'Full Match = %1$s, first Subpattern = %2$s, second Subpattern = %3$s', $matches );