【问题标题】:Why use sprintf function in PHP?为什么在 PHP 中使用 sprintf 函数?
【发布时间】:2010-11-26 01:47:12
【问题描述】:

我正在尝试了解有关 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;

我错过了什么吗?

【问题讨论】:

  • +1,好问题。我自己很少使用它,所以我有兴趣看到答案。
  • 我个人在连接很多东西时使用它。直到那时我才发现它很有用,主要是为了可读性。

标签: php printf


【解决方案1】:

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

例如,指定数字格式(十六进制、十进制、八进制)、小数位数、填充等。谷歌 printf 你会发现很多例子。 wikipedia article on printf 应该可以帮助您入门。

【讨论】:

  • 几年前,我为零填充数字编写了一个不错的函数。在意识到我可以简单地做之前使用了很长时间sprintf('%03d', $num)
  • PHP sprintf 具有所有的格式化功能 ...实际上缺少一个重要的功能,例如%,8d 以获得正确对齐的 8 位数字和逗号分隔的整数。在printf 样式中使用number_format 不太方便。
  • @DisgruntledGoat 你能分解那个格式化字符串('%03d')吗?我认为这真的很有帮助。谢谢。
  • 您还可以重复并重新排列要显示的参数。例如sprintf('%2$s %1$s, my name is also %1$s.', 'World', 'Hello'); 导致 Hello World, my name is also World.
【解决方案2】:

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'

基本上它允许在代码中进行一些分离。如果我在代码中的许多地方使用“你好,我的名字是 %s”,我可以在一个地方将其更改为“%s 是我的名字”,它会自动更新其他任何地方,而无需前往每个实例并四处走动串联。

【讨论】:

  • 这个。现在添加编号参数和翻译:]
  • 但在这种情况下,您可以轻松地使用字符串连接。还是我没抓住重点?
【解决方案3】:

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 来存储本地化字符串,但您明白了。


【讨论】:

    【解决方案4】:

    翻译起来更容易。

    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

    这更有意义,翻译成其他语言也更灵活

    【讨论】:

    • 我知道这在哪里非常有用 +1
    • 虽然对于翻译,使用位置说明符可能会更好,以便在翻译时提供一些额外的灵活性,即:echo sprintf(_("Here is the result: %1$s for this date %2$s"), $result, $date); 这将允许翻译修改像 For the date %2$s, the result is %1$s 这样的短语
    • 尽管它们在原始字符串中不存在,但您可以将它们放入翻译中,它们将起作用。
    【解决方案5】:

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

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

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

    Lang 文件 (EN_US):

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

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

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

    【讨论】:

    • 我从来不用担心处理语言文件,但这对我来说似乎是最有用的用途之一
    • 这个例子对我来说也很有意义,因为我没有做过很多多语言应用程序。
    【解决方案6】:

    为什么要使用它?

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

    zh.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 的全部功能,因此也是格式化多种类型变量的单行器,例如:

    【讨论】:

      【解决方案7】:

      如前所述,它允许对输入数据进行格式化。比如强制 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 插入攻击。 如果您解析未经处理的输入,您的网站和服务器将被黑客入侵。 (当然,完全由您的代码构造并保证安全的变量除外。)

      【讨论】:

      • 该代码易受 SQL 注入攻击。使用占位符,而不是查询插值!
      • @duskwuff - 按原样使用,你是对的。抱歉,我已经修改了答案。
      【解决方案8】:

      使用sprintf() 来格式化您的字符串更加简洁和安全。

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

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

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

      总而言之,使用它是一种很好的做法,可以让代码更清晰、更易读。

      例如,请参阅下面的real example

      $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(),但它输出的是格式化字符串,而不是返回(返回给变量)。

      哪个更易读?

      【讨论】:

      • 我不反对,但也值得一提,我在 6-7 年前问过这个问题,回想起来我不记得从那时起我曾经使用过这个!我确实希望有一天能为它找到一个地方,并且相信它有一个地方,但有趣的是,自从多年前提出这个问题后我就没有使用过它!
      • 我可以说它有点类似于在PHP中为PDO设置变量。此外,如果您想为其提供可读性参数,那么在这种情况下,我更喜欢使用 sprintf() 分成多行,其中带占位符的字符串位于 1 行,而值位于其下方的一行甚至多行,所有当然正确缩进。以我描述的方式使用您的演示...i.imgur.com/DFi3ur8.png
      • Prepared statements 结合存储过程是防止sql注入的最好方法,而不是sprintf。如果字符串恰好包含单引号,您仍然可以使用 sprintf 轻松进行 sql 注入
      【解决方案9】:

      即使我是同样的东西,除非我最近使用它。当您根据用户输入生成文档时,这将派上用场。

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

      WH 可以很容易地写成

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

      【讨论】:

      • 或者你可以这样做:"&lt;p&gt;Some big paragraph {$a['name']} more words {$a['age']}&lt;/p&gt;"。但是,请不要忘记清理用户输入,否则您的代码会出现 XSS 漏洞。
      • 您也可以使用HEREDOC syntax。在我看来,两者都比 sprintf 语法更容易阅读。
      【解决方案10】:

      这个:

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

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

      【讨论】:

        【解决方案11】:

        一些典型情况是您需要更精确地控制输出格式。例如,确保特定值的前面有特定数量的空格填充可能会很棘手,具体取决于其长度,或者以特定的精确格式输出数字。

        有很多例子in the PHP manual

        你的“更容易编写”的例子也是如此。虽然 echo 可能更容易编写,但 sprintf 更容易阅读,尤其是在你有很多变量的情况下。

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

        哦,你的例子实际上是错误的一部分。 sprintf 返回字符串,但 echo 不返回 - echo 立即输出它并且不返回任何内容,而 sprintf 只是返回它。

        【讨论】:

          【解决方案12】:
          1. 如果你用过 C/C++,那么你会习惯 sprintf 函数。

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

          【讨论】:

          • Java 的最新版本将在后台将字符串连接转换为 StringBuilder。
          • PHP 中的字符串不是对象。第二个示例使用语言构造而不是函数调用,因此速度更快。
          【解决方案13】:

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

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

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

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

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

          【讨论】:

          • 使用intval($_GET["article_id"])不是更简单吗?好吧,实际上它需要更多的打字......*记下你的方法“Dag nabbit。
          • 对于这种类型的代码,最好使用内置的mysql_real_escape_string(): $query = sprintf("SELECT * from authors where id = %d;", mysql_real_escape_string($_GET['article_id' ]));
          【解决方案14】:
          define('TEXT_MESSAGE', 'The variable "%s" is in the middle!');
          
          sprintf(TEXT_MESSAGE, "Var1");
          sprintf(TEXT_MESSAGE, "Var2");
          sprintf(TEXT_MESSAGE, "Var3");
          

          【讨论】:

            【解决方案15】:

            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 在格式化使用数字的字符串时特别有用”),这确实是最重要的一点sprintf 在 PHP 中,但随后看到那些令人费解的例子从“缺失”的水果中创造水果 :),每个重复只显示 %d(和 abs(),与主题无关),而不是显示一些过多数字格式选项(是相关的),我改变了主意。但是,如果您更新答案,我仍然会在冰箱中为您投票。 :) 谢谢,干杯!
            【解决方案16】:

            嗯,sprintf 有很多我们知道的功能 一个例子如下:

            几个月前,我需要将秒转换为小时:分钟:秒格式 就像$t = 494050 //seconds 我想像137 h 14 m 10 s 一样打印所以我想出了php 函数springf() 我只是在$techo 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() 函数是非常有用的,如果我们知道如何使用它。

            【讨论】:

              【解决方案17】:

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

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

              【讨论】:

                【解决方案18】:

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

                【讨论】:

                  【解决方案19】:

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

                  【讨论】:

                    【解决方案20】:

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

                    在你的情况下,你有

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

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

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

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

                    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
                    

                    【讨论】:

                      【解决方案21】:

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

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

                      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"
                      

                      希望对您有所帮助。

                      【讨论】:

                        【解决方案22】:

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

                        printf() 输出

                        sprintf() 退货

                        【讨论】:

                          【解决方案23】:

                          在循环中使用 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

                          '$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

                          【讨论】:

                            【解决方案24】:

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

                            $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?'
                            

                            或者

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

                            当然这是一个额外的步骤,但是如果你的条件检查做了一堆其他功能性的事情,那么引号和附加就会开始妨碍功能性编码。

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2021-06-25
                              • 1970-01-01
                              • 2020-12-18
                              • 2012-09-26
                              • 1970-01-01
                              • 1970-01-01
                              • 2022-05-16
                              相关资源
                              最近更新 更多