我试图了解更多关于PHP函数sprintf(),但php.net没有帮助我,因为我仍然困惑,为什么要使用它?

看看下面的例子。

为什么用这个:

$output = sprintf("Here is the result: %s for this date %s", $result, $date);

当这样做是一样的,更容易写IMO:

$output = 'Here is the result: ' .$result. ' for this date ' .$date;

我是不是遗漏了什么?


Sprintf具有原始printf的所有格式化功能,这意味着除了在字符串中插入变量值外,您还可以做更多的事情。

例如,指定数字格式(十六进制,十进制,八进制),小数的数量,填充等等。谷歌用于printf,你会找到很多例子。你应该从维基百科上关于printf的文章开始。


在一些典型的情况下,您需要更精确地控制输出格式。例如,根据特定值的长度,确保特定值前面填充了特定数量的空格,或者以特定的精确格式输出数字,这可能很棘手。

在PHP手册中有很多例子

还有你的“更容易写”的例子。虽然echo可能更容易编写,但sprintf更容易读取,特别是在有很多变量的情况下。

使用sprintf或printf的另一个原因可能是您希望让用户定义某些值的输出格式—您可以安全地允许他们定义与sprintf兼容的输出格式。

哦,你的例子有一部分是错的。Sprintf返回字符串,但echo不返回——echo立即输出它并且不返回任何东西,而Sprintf只是返回它。


如果您使用过C/ c++,那么您应该已经习惯了sprintf函数。 很有可能第二条线的效率较低。Echo被设计为输出命令,而sprintf被设计为执行字符串令牌替换。我不是PHP专家,但我怀疑回声涉及到更多的对象。如果它像Java那样工作,那么每当有东西添加到列表中时,它都会创建一个新的字符串,因此最终会创建4个字符串。


sprintf有许多用例,但我使用它们的一种方式是在数据库中存储这样的字符串:'Hello, My Name is %s',或将其作为PHP类中的常量。这样当我想要使用该字符串时,我可以简单地这样做:

$name = 'Josh';
// $stringFromDB = 'Hello, My Name is %s';
$greeting = sprintf($stringFromDB, $name);
// $greetting = 'Hello, My Name is Josh'

本质上,它允许在代码中进行一些分离。如果我在代码中的许多地方使用“Hello, My Name is %s”,我可以在一个地方将其更改为“%s是我的名字”,并且它会自动更新其他任何地方,而不必去到每个实例并在连接中移动。


有时我会得到这样的东西,我认为这更容易阅读:

$fileName = sprintf('thumb_%s.%s', 
                    $fileId,
                    $fileInfo['extension']);

sprintf的另一个用途是在本地化的应用程序中,因为sprintf的参数不必按照它们在格式字符串中出现的顺序排列。

例子:

$color = 'blue';
$item = 'pen';

sprintf('I have a %s %s', $color, $item);

但是像法语这样的语言对单词的顺序是不同的:

$color = 'bleu';
$item = 'stylo';

sprintf('J\'ai un %2$s %1$s', $color, $item);

(是的,我的法语很烂:我在学校学过德语!)

实际上,您可以使用gettext来存储本地化的字符串,但您可以理解。



我发现的最好的理由是,它允许你把所有的语言字符串放在你的语言文件中,人们可以根据需要翻译和排序它们-但你仍然知道,无论字符串的格式是什么-你希望显示用户的名字。

例如,您的网站会在页面顶部显示“欢迎返回[[User]]”。作为程序员,你不知道或不关心UI人员将如何编写它——你只知道用户名将显示在消息中的某个地方。

因此,您可以将消息嵌入到代码中,而不必担心该消息实际上是什么。

只需文件 (EN_US):

...
$lang['welcome_message'] = 'Welcome back %s';
...

然后,通过在实际的php代码中使用它,就可以支持任何语言的任何类型的消息。

sprintf($lang['welcome_message'], $user->name())

这样更容易翻译。

echo _('Here is the result: ') . $result . _(' for this date ') . $date;

翻译(gettext)字符串现在是:

结果如下: 在这个日期

当翻译成其他语言时,这可能是不可能的,或者会导致非常奇怪的句子。

如果你有

echo sprintf(_("Here is the result: %s for this date %s"), $result, $date);

翻译(gettext)字符串现在是:

结果如下:此日期%s的%s

哪个更有意义,翻译成其他语言更灵活


我通常使用sprintf来确保来自用户输入的id是一个整数,例如:

// is better use prepared statements, but this is practical sometimes
$query = sprintf("SELECT * from articles where id = %d;",$_GET['article_id']);

Also是用来做基本的模板(html邮件或其他东西),所以你可以在很多地方重用模板:

$mail_body = "Hello %s, ...";
$oneMail = sprintf($mail_body, "Victor");
$anotherMail = sprintf($mail_body, "Juan");

用不同的表示形式(八进制、控制小数点等)格式化数字也非常有用。


如前所述,它允许格式化输入数据。例如,强制2dp, 4位数字等。它对于构建MySQL查询字符串非常有用。

另一个优点是,它允许将字符串的布局与输入的数据分开,就像输入参数一样。例如,在MySQL查询的情况下:

// For security, you MUST sanitise ALL user input first, eg:
$username = mysql_real_escape_string($_POST['username']); // etc.
// Now creating the query:
$query = sprintf("INSERT INTO `Users` SET `user`='%s',`password`='%s',`realname`='%s';", $username, $passwd_hash, $realname);

当然,这种方法还有其他用途,比如将输出打印为HTML等。

编辑:出于安全原因,当使用上述技术时,必须在使用此方法之前使用mysql_real_escape_string()清除所有输入变量,以防止MySQL插入攻击。如果你解析未经处理的输入,你的网站和服务器就会被黑客攻击。(当然,那些完全由你的代码构造并保证安全的变量除外。)


define('TEXT_MESSAGE', 'The variable "%s" is in the middle!');

sprintf(TEXT_MESSAGE, "Var1");
sprintf(TEXT_MESSAGE, "Var2");
sprintf(TEXT_MESSAGE, "Var3");

即使我认为同样的事情,除非我最近使用它。当您根据用户输入生成文档时,这将很方便。

"<p>Some big paragraph ".$a["name"]." again have tot ake care of space and stuff .". $a["age"]. "also would be hard to keep track of punctuations and stuff in a really ".$a["token"]. paragarapoh.";

WHich可以很容易地写成

sprintf("Some big paragraph %s. Again have to take care of space and stuff.%s also wouldnt be hard to keep track of punctuations and stuff in a really %s paragraph",$a,$b,$c);

参数与使用模板的参数相同。您需要将Textsleev与实际的变量值分开。除了我们提到的sprintf的额外功能之外,它只是一个样式问题。


使用sprintf的一个非常好的用例是输出填充格式的数字,以及在字符串中混合不同类型的数字。在许多情况下,它更容易阅读,并且可以超级简单地打印同一变量的不同表示形式,特别是数值形式。


这样的:

"<p>Some big paragraph ".$a["name"]." again have to take care of space and stuff .". $a["age"]. "also would be hard to keep track of punctuations and stuff in a really ".$a["token"]. paragraph.";

也可以写成:

"<p>Some big paragraph {$a['name']} again have to take care of space and stuff .{$a['age']} also would be hard to keep track of punctuations and stuff in a really {$a['token']} paragraph.";

在我看来,这更容易理解,但我可以看到本地化或格式化的用途。


Sprintf在格式化使用数字的字符串时特别有用。例如,

$oranges = -2.34;
echo sprintf("There are %d oranges in the basket", $oranges);

Output: There are -2 oranges in the basket

Oranges被格式化为整数(-2),但如果使用%u表示无符号值,则会换行为正数。为了避免这种行为,我使用绝对函数abs(),将数字四舍五入,如下所示:

$oranges = -5.67;
echo sprintf("There are %d oranges in the basket", abs($oranges));

Output: There are 5 oranges in the basket

最终的结果是一个具有高可读性、逻辑结构、清晰格式和根据需要灵活添加额外变量的语句。随着变量数量的增加以及操作这些变量的函数的组合,好处变得更加明显。最后一个例子:

$oranges = -3.14;
$apples = 1.5;
echo sprintf("There are %d oranges and %d apples", abs($oranges), abs($apples));

Output: There are 3 oranges and 4 apples

sprintf语句的左边清楚地表达了字符串和期望值的类型,而右边清楚地表达了使用的变量以及如何操作它们。


众所周知,sprintf有很多功能 一个例子如下:

几个月前,我需要将秒转换为小时:分钟:秒的格式 像$t = 494050 //秒,我想打印像137 h 14 m 10 s,所以我提出了php函数springf(),我只是保持秒在$t和回声sprintf(“%02d h %s%02d m %s%02d s”,地板($t/3600), $f, ($t/60)%60, $f, $t%60);得到137小时14米10秒

Sprintf()函数是一个非常有用的函数,如果我们知道如何使用它。


你为什么要用它?

在为语言字符串使用(外部)源时,它被证明非常有用。如果你在一个给定的多语言字符串中需要固定数量的变量,你只需要知道正确的顺序:

en.txt

not_found    = "%s could not be found."
bad_argument = "Bad arguments for function %s."
bad_arg_no   = "Bad argument %d for function %s."

hu.txt

not_found    = "A keresett eljárás (%s) nem található."
bad_argument = "Érvénytelen paraméterek a(z) %s eljárás hívásakor."
bad_arg_no   = "Érvénytelen %d. paraméter a(z) %s eljárás hívásakor."

在多种语言中,插入的变量甚至不必在开头或结尾,只是它们的顺序很重要。

当然,你可以编写自己的函数来执行这个替换,毫无疑问,甚至会有一些小的性能提升,但它要快得多(假设你有一个类Language来读取语言字符串):

/**
 * throws exception with message($name = "ExampleMethod"):
 *  - using en.txt: ExampleMethod could not be found.
 *  - using hu.txt: A keresett eljárás (ExampleMethod) nem található.
 */
throw new Exception(sprintf(Language::Get('not_found'), $name));

/**
 * throws exception with message ($param_index = 3, $name = "ExampleMethod"):
 *  - using en.txt: Bad argument 3 for function ExampleMethod.
 *  - using hu.txt: Érvénytelen 3. paraméter a(z) ExampleMethod eljárás hívásakor.
 */
throw new Exception(sprintf(Language::Get('bad_arg_no'), $param_index, $name));

它还带有printf的全部功能,因此也是格式化多种类型变量的一行程序,例如:

浮点数输出精度,或 用前导零填充整数。


使用sprintf()格式化字符串更干净、更安全。

例如,在处理输入变量时,可以通过提前指定预期格式(例如,您期望字符串[%s]或数字[%d])来防止意外的意外。这可能有助于潜在的SQL注入风险,但它不会阻止如果字符串包含引号。

它还有助于处理浮点数,你可以显式地指定数字精度(例如%.2f),这可以节省你使用转换函数。

另一个优点是,大多数主流编程语言都有自己的sprintf()实现,因此一旦熟悉了它,它就更容易使用,而不是学习一门新的语言(比如如何连接字符串或转换浮点数)。

总之,这是一种很好的实践,可以让代码更清晰、更易读。

例如,请看下面的真实例子:

$insert .= "('".$tr[0]."','".$tr[0]."','".$tr[0]."','".$tr[0]."'),";

或者一些简单的例子,打印。' 1 ', ' 2 ', ' 3 ', ' 4 ':

print "foo: '" . $a . "','" . $b . "'; bar: '" . $c . "','" . $d . "'" . "\n";

和打印格式化字符串:

printf("foo: '%d','%d'; bar: '%d','%d'\n", $a, $b, $c, $d);

其中printf()相当于sprintf(),但它输出一个格式化的字符串而不是返回它(给变量)。

哪个更有可读性?


使用sprintf()函数而不是普通连接的优点是,您可以对要连接的变量应用不同类型的格式。

在你的情况下,你做到了

$output = sprintf("Here is the result: %s for this date %s", $result, $date);

and

$output = 'Here is the result: ' .$result. ' for this date ' .$date;

让我们输入$result = 'passed';日期= '23 ';

使用普通的连接,你只能得到输出:

Here is the result: passed for this date 23rd

然而,如果你使用sprintf(),你可以得到一个修改后的输出,如:

$output = sprintf('Here is the result: %.4s for this date %.2s',$result,$date);
echo $output;

输出:

Here is the result: pass for this date 23

Sprintf()非常类似于printf()。如果您了解printf()的详细信息,那么sprintf()甚至vsprintf()都不难理解。

sprintf()与printf()的一个不同之处在于,您需要声明一个变量来捕获函数的输出,因为它不直接打印/回显任何内容。让我们看看下面的代码片段:

printf("Hello %s", "world"); // "Hello world"

sprintf("Hello %s", "world"); // does not display anything

echo sprintf("Hello %s", "world"); // "Hello world"

$a = sprintf("Hello %s", "world"); // does not display anything

echo $a;// "Hello world"

希望这能有所帮助。


一个是“输出”,另一个是“返回”,这是主要的区别之一。

printf()输出

sprintf()返回


在循环中使用sprintf时必须小心:

$a = 'Anton';
$b = 'Bert';
$c = 'Corni';
$d = 'Dora';
$e = 'Emiel';
$f = 'Falk';
$loops = 10000000;

$time = microtime(true);

for ($i = 0; $i < $loops; $i++)
{
    $test = $a . $b . $c . $d . $e . $f;
}

$concatTime = microtime(true) - $time;

$time = microtime(true);

for ($i = 0; $i < $loops; $i++)
{
    $test = "$a $b $c $d $e $f";
}

$concat2Time = microtime(true) - $time;

$time = microtime(true);

for ($i = 0; $i < $loops; $i++)
{
    $test = sprintf('%s %s %s %s %s %s', $a, $b, $c, $d, $e, $f);
}

$sprintfTime = microtime(true) - $time;

echo 'Loops: ' . $loops . '<br>';
echo '\'$a . $b . $c . $d . $e . $f\'' . ' needs ' . $concatTime  . 's<br>';
echo '"$a $b $c $d $e $f"' . ' needs ' . $concat2Time  . 's<br>';
echo 'sprintf(\'%s %s %s %s %s %s\', $a, $b, $c, $d, $e, $f)' . ' needs ' . $sprintfTime  . 's<br>';

这会导致以下时间(在我的本地机器上使用PHP 7.2):

循环:10000000

“一美元。b美元。$ c。美元d。$ e。$f'需要1.4507689476013s

“$a $b $c $d $e $f”需要1.9958319664001s

sprintf (' % s % s % s % s % s % s’,一个美元,美元b, c,美元美元d, e,美元$ f)需要9.1771278381348秒


我用它来发送给用户的消息或其他“漂亮”类型的功能。例如,如果我知道我将使用用户的名字。

$name = 'Some dynamic name';

并在这种情况下使用多个消息。(例如,屏蔽或关注其他用户)

$messageBlock = 'You have blocked %s from accessing you.';
$messageFollow = 'Following %s is a great idea!';

你可以创建一个通用函数,对用户做一些事情,并添加这个字符串,无论句子的结构是什么,它都应该看起来很漂亮。我总是不喜欢仅仅把字符串附加在一起,不断地使用点符号,关闭和重新打开字符串,只是为了让一个句子看起来更好。我一开始是一个粉丝,像大多数人一样,但这似乎非常有用,当多个字符串需要被操纵,你不想硬编码变量的位置在每次。

想想看,哪个更好看?

return $messageOne === true ? $name.'. Please use the next example' : 'Hi '.$name.', how are you?'

Or

$message = $messageOne === true ? 'Option one %s' 
: ($messageTwo === true ? 'Option Two %s maybe?' : '%s you can choose from tons of grammatical instances and not have to edit variable placement and strings');

return sprintf($message, $name);

当然,这是一个额外的步骤,但如果你的条件检查做了一堆其他功能的事情,然后引号和追加开始在编码的功能。