关于printf:为什么在PHP中使用sprintf函数?

Why use sprintf function in PHP?

我正在尝试学习有关PHP函数sprintf()的更多信息,但由于我仍然感到困惑,php.net并没有太大帮助,您为什么要使用它?

看下面我的例子。

为什么使用这个:

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

当这样做相同并且更容易编写IMO时:

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

我在这里想念什么吗?


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

例如,指定数字格式(十六进制,十进制,八进制),小数位数,填充等。 Google for printf,您会发现很多例子。关于printf的维基百科文章应该可以帮助您入门。


sprintf有很多用例,但是我使用它们的一种方式是通过在数据库中存储这样的字符串:"你好,我的名字是%s"或在PHP类中作为常量。这样,当我想使用该字符串时,我可以简单地做到这一点:

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

本质上,它允许代码中的某些分离。如果我在代码中的很多地方都使用" Hello,我的名字是%s",则可以在一个地方将其更改为"%s是我的名字",并且它会自动更新其他所有地方,而不必转到每个实例并四处移动串联。


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

例:

1
2
3
4
$color = 'blue';
$item = 'pen';

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

但是像法语这样的语言对单词的排序却有所不同:

1
2
3
4
$color = 'bleu';
$item = 'stylo';

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

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

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


翻译起来更容易。

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

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

  • 结果如下:
  • 在这个日期

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

现在如果你有

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

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

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

这更有意义,而且翻译成其他语言要灵活得多。


我发现的最好原因是,它允许您将所有语言字符串放入您的语言文件中,以便人们可以根据需要对其进行翻译和排序-但您仍然知道,无论字符串采用哪种格式-您都希望显示用户名。

例如,您的网站在页面顶部会显示"欢迎回来[[用户]]"。作为程序员,您不知道或不在乎UI家伙将如何编写-您只知道用户名将显示在消息中的某个位置。

因此,您确实可以将消息嵌入代码中,而不必担心该消息的实际含义。

郎文件(EN_US):

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

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

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


why would you want to use it?

当使用(外部)语言字符串源时,它非常有用。如果在给定的多语言字符串中需要固定数量的变量,则只需知道正确的顺序即可:

en.txt

1
2
3
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

1
2
3
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来读取语言字符串):

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 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的全部功能,因此还是格式化多种类型的变量的单行代码,例如:

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

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

另一个优点是,它允许您将字符串的布局与正在馈入其中的数据分开,就像在参数中馈入一样。例如,对于MySQL查询:

1
2
3
4
// 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插入攻击。如果您解析未经验证的输入,则您的站点和服务器将被黑客入侵。 (当然,除了那些完全由您的代码构造并保证安全的变量之外)。


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

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

这也有助于处理浮点数,您可以显式指定位数精度(例如%.2f),从而避免使用转换函数。

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

总而言之,使用好习惯是为了使代码更简洁,可读性更好。

例如,请参见下面的实际示例:

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

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

1
2
print"foo: '" . $a ."','" . $b ."'; bar: '" . $c ."','" . $d ."'" ."
"
;

并使用格式化的字符串打印:

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

其中printf()等效于sprintf(),但是它输出格式化的字符串,而不是将其返回(返回到变量)。

哪个更易读?


除非我最近使用过,否则即使我也一样。当您根据用户输入生成文档时,这将很方便。

1
2
"<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.";

可以很容易地写成

1
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);


这个:

1
2
"<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.";

也可以写成:

1
2
"<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."
;

我认为这更容易阅读,但是我可以看到它用于本地化或格式化。


在某些典型情况下,您需要对输出格式进行更精确的控制。例如,要确保一个特定值的长度取决于它的长度,或者确保以特定的精确格式输出一个数字,这可能很棘手。

PHP手册中有很多示例

还有示例"易于编写"的内容。尽管回声可能更易于编写,但sprintf却更易于阅读,尤其是在您有很多变量的情况下。

使用sprintf或printf的另一个原因可能是您想让用户定义某个值的输出格式-您可以放心地允许他们定义sprintf兼容的输出格式。

哦,您的示例实际上是错误的。 sprintf返回字符串,但是echo不返回-echo立即输出它,什么也不返回,而sprintf只是返回它。


  • 如果您使用过C / C ++,那么您将习惯于sprintf函数。

  • 第二行很有可能效率较低。 Echo被设计为输出命令,而sprintf被设计为进行字符串令牌替换。我不是PHP人士,但我怀疑回声涉及更多对象。如果它的行为类似于Java,则每次将新内容添加到列表中时都会创建一个新字符串,因此最终将创建4个字符串。


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

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

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

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

    也用于做基本模板(用于html邮件或其他东西),因此您可以在许多地方重复使用模板:

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

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


    1
    2
    3
    4
    5
    define('TEXT_MESSAGE', 'The variable"%s" is in the middle!');

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

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

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

    Output: There are -2 oranges in the basket

    桔子的格式为整数(-2),但是如果将%u用作无符号值,则桔子将环绕成正数。为了避免这种行为,我使用绝对函数abs()将数字四舍五入为零,如下所示:

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

    Output: There are 5 oranges in the basket

    最终结果是一条语句具有很高的可读性,逻辑结构,清晰的格式,并且可以根据需要灵活地添加其他变量。随着变量数量的增加以及与操纵这些变量的功能的结合,收益变得更加明显。最后一个例子:

    1
    2
    3
    4
    5
    $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 //seconds一样,我想像137 h 14 m 10 s一样打印,所以我想出了php函数springf(),我只是将$t保留了几秒钟,而echo sprintf("%02d h %s%02d m %s%02d s", floor($t/3600), $f, ($t/60)%60, $f, $t%60);给了我137 h 14 m 10 s

    如果我们知道如何使用sprintf()函数,它将非常有用。


    使用sprintf的一个非常好的用例是输出带数字的填充格式,以及在字符串中混合使用不同类型时。在许多情况下,它可能更易于阅读,并且使打印同一变量的不同表示形式(尤其是数字表示形式)变得非常简单。


    在普通的串联上使用sprintf()函数的优点是,可以对要串联的变量应用不同类型的格式。

    就你而言

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

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

    让我们以$result = 'passed'; date = '23rd';

    使用普通的串联,您只能得到输出:

    1
    Here is the result: passed for this date 23rd

    但是,如果使用sprintf(),则可以得到修改后的输出,例如:

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

    输出:

    1
    Here is the result: pass for this date 23


    该参数与使用模板的参数相同。您需要将Textsleev与实际变量值分开。除了我们提到的sprintf的其他功能之外,这还只是一种样式。


    sprintf()printf()非常相似。如果您详细了解printf(),那么sprintf()甚至vsprintf()并不是很难理解。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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"

    希望能有所帮助。


    我将其用于发送给用户或其他"漂亮"功能的消息。例如,如果我知道我将使用用户名。

    1
    $name = 'Some dynamic name';

    并在这种情况下使用多个消息。 (即阻止或关注另一个用户)

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

    您可以创建一个对用户执行某些操作的通用函数,并添加此字符串,无论句子的结构如何,它看起来都非常不错。我总是不喜欢仅将字符串附加在一起,并不断使用点符号以及关闭和重新打开字符串以使句子看起来不错。我一开始像大多数粉丝一样,但是当需要操纵多个字符串并且您不想每次都硬编码变量的位置时,这似乎很有用。

    想一想,看起来更好的是什么?

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

    要么

    1
    2
    3
    4
    $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);

    当然,这是一个额外的步骤,但是如果您的条件检查执行了许多其他功能操作,那么引号和附加内容就会开始妨碍功能编码的方式。


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

    printf()输出

    sprintf()返回


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    $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 . '';
    echo '\'$a . $b . $c . $d . $e . $f\'' . ' needs ' . $concatTime  . 's';
    echo '"$a $b $c $d $e $f"' . ' needs ' . $concat2Time  . 's';
    echo 'sprintf(\'%s %s %s %s %s %s\', $a, $b, $c, $d, $e, $f)' . ' needs ' . $sprintfTime  . 's';

    这导致以下情况(在我的本地计算机上使用PHP 7.2):

    循环次数:10000000

    '$ a。 $ b。 $ c。 $ d。 $ e。 $ f'需要1.4507689476013s

    " $ a $ b $ c $ d $ e $ f"需要1.9958319664001s

    sprintf('%s%s%s%s%s%s',$ a,$ b,$ c,$ d,$ e,$ f)需要9.1771278381348s