¿Por qué usar la función sprintf en PHP?

188

Estoy tratando de aprender más sobre la función sprintf () de PHP, pero php.net no me ayudó mucho ya que todavía estoy confundido, ¿por qué querrías usarlo?

Echa un vistazo a mi ejemplo a continuación.

Por qué usar esto:

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

Cuando esto hace lo mismo y es más fácil escribir IMO:

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

¿Me estoy perdiendo de algo?

JasonDavis
fuente
40
+1, buena pregunta. Raramente lo uso, así que estoy interesado en ver las respuestas.
zombat
66
Tenga en cuenta que sus dos ejemplos no son equivalentes: el primero simplemente asigna el resultado $output, mientras que el segundo echoes el mismo.
Daniel Pryden
Personalmente lo uso cuando estoy concatenando muchas cosas. Es solo entonces cuando lo encuentro útil, principalmente para la legibilidad.
AlexMorley-Finch

Respuestas:

130

sprintf tiene todas las capacidades de formato del printf original, lo que significa que puede hacer mucho más que simplemente insertar valores variables en cadenas.

Por ejemplo, especifique el formato de número (hexadecimal, decimal, octal), número de decimales, relleno y más. Google para printf y encontrarás muchos ejemplos. El artículo de wikipedia sobre printf debería ayudarlo a comenzar.

Isak Savo
fuente
46
Hace años escribí una buena función para los números de relleno cero. Utilizado para las edades antes de darse cuenta que sólo podía hacersprintf('%03d', $num)
DisgruntledGoat
55
PHP sprintf tiene todas las capacidades de formateo ... en realidad falta uno importante, %,8dpor ejemplo, para obtener el entero de 8 dígitos correctamente alineado y separado por comas. Usar number_formates menos conveniente en un printfestilo.
e2-e4
3
@DisgruntledGoat ¿Puedes desglosar esa cadena de formato ('% 03d')? Creo que sería realmente útil. Gracias.
Sr. Smee
3
También puede repetir y reordenar los argumentos para mostrar. por ejemplo, sprintf('%2$s %1$s, my name is also %1$s.', 'World', 'Hello');lo que resulta enHello World, my name is also World.
fyrye
77

Hay muchos casos de uso para sprintf, pero una forma en que los uso es almacenando una cadena como esta: 'Hola, mi nombre es% s' en una base de datos o como una constante en una clase de PHP. De esa manera, cuando quiero usar esa cadena, simplemente puedo hacer esto:

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

Básicamente, permite cierta separación en el código. Si uso 'Hola, mi nombre es% s' en muchos lugares de mi código, puedo cambiarlo a '% s es mi nombre' en un lugar y se actualiza automáticamente en cualquier otro lugar, sin tener que ir a cada instancia y moverse. concatenaciones

macinjosh
fuente
8
Esta. Ahora agregue argumentos numerados y traducciones:]
Konerak
46

Otro uso de sprintfes en aplicaciones localizadas como argumentos para sprintfno tener que estar en el orden en que aparecen en la cadena de formato.

Ejemplo:

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

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

Pero un idioma como el francés ordena las palabras de manera diferente:

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

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

(Sí, mi francés apesta: ¡aprendí alemán en la escuela!)

En realidad, usaría gettext para almacenar las cadenas localizadas, pero se entiende la idea.


Ken Keenan
fuente
37

Es más fácil traducir.

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

Las cadenas de traducción (gettext) ahora son:

  • Aquí está el resultado:
  • para esta fecha

Cuando se traduce a otro idioma, puede ser imposible o resultar en oraciones muy extrañas.

Ahora si tienes

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

Las cadenas de traducción (gettext) ahora son:

  • Aquí está el resultado:% s para esta fecha% s

Lo que tiene mucho más sentido y es mucho más flexible para traducir a otros idiomas

raspi
fuente
1
Veo dónde esto sería muy útil +1
JasonDavis
66
Aunque para las traducciones probablemente sería mejor usar especificadores de posición, para permitir cierta flexibilidad adicional al traducir, es decir: echo sprintf(_("Here is the result: %1$s for this date %2$s"), $result, $date);Eso permitiría que una traducción modifique la frase comoFor the date %2$s, the result is %1$s
PatrikAkerstrand
1
Aunque incluso si no están presentes en la cadena original, puede ponerlos en la traducción y funcionarán.
espectras
17

La mejor razón que he encontrado es que le permite colocar todas las cadenas de idioma en su archivo de idioma donde las personas pueden traducirlas y ordenarlas según sea necesario; sin embargo, todavía sabe que, sin importar en qué formato esté la cadena, desea mostrar El nombre del usuario.

Por ejemplo, su sitio dirá "Bienvenido de nuevo [[Usuario]]" en la parte superior de la página. Como programador, no sabes ni te importa cómo van a escribir eso los chicos de la interfaz de usuario; solo sabes que el nombre de un usuario se mostrará en algún lugar de un mensaje.

Por lo tanto, puede incrustar el mensaje en su código sin preocuparse por lo que realmente es ese mensaje.

Archivo Lang (EN_US):

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

Entonces puede admitir cualquier tipo de mensaje en cualquier idioma al usar esto en su código php real.

sprintf($lang['welcome_message'], $user->name())
Xeoncross
fuente
Nunca he tenido que preocuparse de hacer los archivos de idioma, pero esto parece como una de las aplicaciones más útiles para mí
JasonDavis
Este ejemplo también tiene mucho sentido para mí, ya que no he hecho muchas aplicaciones de múltiples idiomas.
MKN Web Solutions
9

¿por qué quieres usarlo?

Es muy útil cuando se usa una fuente (externa) para cadenas de idioma. Si necesita un número fijo de variables en una cadena multilingüe dada, solo necesita saber el orden correcto:

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

Las variables insertadas ni siquiera tienen que estar al principio o al final en varios idiomas, solo su orden importa.

Por supuesto, podría escribir su propia función para realizar este reemplazo, sin duda, incluso con algunos aumentos menores de rendimiento, pero es mucho más rápido simplemente (dado que tiene una clase Languagepara leer cadenas de idioma) :

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

También viene con todas las capacidades de printf, por lo tanto, también es una línea para formatear numerosos tipos de variables, por ejemplo:

John Weisz
fuente
8

Como se mencionó, permite formatear los datos de entrada. Por ejemplo, forzar números de 2dp, 4 dígitos, etc. Es bastante útil para construir cadenas de consulta MySQL.

Otra ventaja es que le permite separar el diseño de la cadena de los datos que se introducen en ella, casi como la alimentación en los parámetros. Por ejemplo, en el caso de una consulta 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);

Por supuesto, este método tiene otros usos, como cuando se imprime como HTML, etc.

Editar: por razones de seguridad , al usar una técnica como la anterior , debe desinfectar todas las variables de entrada mysql_real_escape_string()antes de usar este método , para evitar ataques de inserción de MySQL. Si analiza la entrada sin desinfectar, su sitio y servidor serán pirateados . (Con la excepción de, por supuesto, las variables que han sido completamente construidas por su código y se garantiza que sean seguras).

Samuel Jaeschke
fuente
2
Ese código es vulnerable a la inyección de SQL. ¡Utilice marcadores de posición, no interpolación de consultas!
duskwuff -inactive-
@duskwuff - Usado como está, tienes razón. Lo siento, he modificado mi respuesta.
Samuel Jaeschke, el
7

Usar sprintf () es mucho más limpio y seguro para formatear su cadena.

Por ejemplo, cuando se trata de variables de entrada, evita sorpresas inesperadas al especificar el formato esperado de antemano (por ejemplo, que espera una cadena [ %s] o el número [ %d]). Potencialmente, esto podría ayudar con el posible riesgo de inyección de SQL , pero no evitará si las cadenas consisten en comillas.

También ayuda a tratar con flotantes, puede especificar explícitamente la precisión de los dígitos (por ejemplo %.2f) que le ahorra el uso de funciones de conversión.

La otra ventaja es que la mayoría de los principales lenguajes de programación tienen su propia implementación sprintf(), por lo que una vez que te familiarizas con él, es aún más fácil de usar, en lugar de aprender un nuevo lenguaje (como cómo concatenar cadenas o convertir los flotantes).

En resumen, es una buena práctica usar para tener un código más limpio y más legible.

Por ejemplo, vea el siguiente ejemplo real :

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

O algún ejemplo simple que imprima, por ejemplo '1','2','3','4':

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

e impresión con cadena formateada:

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

donde printf()es equivalente a sprintf(), pero genera una cadena con formato en lugar de devolverla (a la variable).

¿Cuál es más legible?

kenorb
fuente
1
No estoy en desacuerdo, pero también vale la pena mencionar que pregunté esto hace 6-7 años y, recordando, ¡no puedo recordar una vez que he usado esto desde entonces! Espero encontrar un lugar para él algún día y creo que tiene un lugar pero es divertido pensar que no lo he usado desde que hice esta pregunta hace muchos años.
JasonDavis
Puedo decir que es algo similar a la configuración de variables para PDO en PHP. Además, si desea darle el argumento de legibilidad, entonces en ese caso prefiero usar sprintf()líneas divididas en múltiples líneas donde la cadena con marcadores de posición está en 1 línea y los valores están en una línea o incluso en varias líneas debajo de ella, todo sangrado correctamente de curso. Usando su demo de la manera que describí ... i.imgur.com/DFi3ur8.png
JasonDavis
Las declaraciones preparadas junto con los procedimientos almacenados son la mejor manera de prevenir la inyección de sql, no sprintf. Todavía puede inyectar fácilmente sql usando sprintf, si la cadena contiene una comilla simple
HumbleWebDev
4

Aunque pensé lo mismo a menos que lo haya usado recientemente. Cuando genere documentos basados ​​en las entradas del usuario, esto será útil.

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

Que se puede escribir fácilmente como

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);
pez espada
fuente
O bien, puede hacer esto: "<p>Some big paragraph {$a['name']} more words {$a['age']}</p>". SIN EMBARGO, NO OLVIDE desinfectar la entrada del usuario o abra su código hasta vulnerabilidades XSS.
thirdender
También puede usar la sintaxis HEREDOC . En mi opinión, ambos son más fáciles de leer que la sintaxis sprintf.
thirdender
4

Esta:

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

También podría escribirse:

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

En mi opinión, esto es más claro de leer, pero puedo ver el uso para localizar o formatear.

Thomas Tremain
fuente
3

Algunos de los casos típicos son donde necesita un control más preciso sobre el formato de salida. Puede ser complicado, por ejemplo, asegurarse de que un valor específico tenga una cantidad específica de espacios acolchados al frente, dependiendo de su longitud, o que un número se emita en un formato preciso específico.

Hay muchos ejemplos en el manual de PHP

También lo que viene a su ejemplo de "más fácil de escribir". Si bien el eco puede ser más fácil de escribir, el sprintf es más fácil de leer, especialmente si tiene muchas variables.

Otra razón para usar sprintf o printf podría ser que desee permitir que un usuario defina el formato de salida de algún valor; puede permitirle de manera segura que defina un formato de salida compatible con sprintf.

Ah, y su ejemplo es realmente incorrecto para una parte. sprintfdevuelve la cadena, pero echono lo hace: echola emite inmediatamente y no devuelve nada, mientras que sprintfsolo la devuelve.

Jani Hartikainen
fuente
3
  1. Si ha usado C / C ++, entonces estaría acostumbrado a la función sprintf.

  2. Existe una buena posibilidad de que la segunda línea sea menos eficiente. Echo está diseñado como un comando de salida, mientras que sprintf está diseñado para hacer una sustitución de token de cadena. No soy una persona PHP, pero sospecho que hay más objetos involucrados con el eco. Si actúa como lo haría Java, crea una nueva cadena cada vez que se agrega algo a la lista, por lo que terminaría con 4 cadenas creadas.

Pablo
fuente
Las ediciones recientes de Java convertirán la concatenación de String en un StringBuilder detrás de escena.
RodeoClown
1
Las cadenas en PHP no son objetos. El segundo ejemplo utiliza una construcción de lenguaje en lugar de una llamada a función, por lo que será imperceptiblemente más rápido.
Tamlyn
3

A veces tengo algo como esto, que considero un poco más fácil de leer:

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

Por lo general, uso sprintf para garantizar que una identificación que proviene de la entrada del usuario sea un número entero, por ejemplo:

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

También se usa para hacer plantillas rudimentarias (para correos html u otras cosas), por lo que puede reutilizar la plantilla en muchos lugares:

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

También es muy útil para formatear números en diferentes representaciones (octal, controlar el lugar decimal, etc.).

Castro
fuente
¿No es más fácil usar intval ($ _ GET ["article_id"])? Bueno, en realidad tomó más escribir ... * anota tu método "Dag nabbit.
user97410
Para este tipo de código, use mejor el mysql_real_escape_string (): $ query = sprintf ("SELECT * from articles where id =% d;", ​​mysql_real_escape_string ($ _ GET ['article_id']));
bruno.braga
3
define('TEXT_MESSAGE', 'The variable "%s" is in the middle!');

sprintf(TEXT_MESSAGE, "Var1");
sprintf(TEXT_MESSAGE, "Var2");
sprintf(TEXT_MESSAGE, "Var3");
Mee
fuente
3

sprintf es particularmente útil al formatear cadenas que usan números. Por ejemplo,

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

Output: There are -2 oranges in the basket

Las naranjas están formateadas como un número entero (-2), pero se ajustarán a números positivos si se usa% u para valores sin signo. Para evitar este comportamiento, uso la función absoluta, abs (), para redondear el número hacia cero de la siguiente manera:

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

Output: There are 5 oranges in the basket

El resultado final es una declaración con alta legibilidad, construcción lógica, formato claro y flexibilidad para agregar variables adicionales según sea necesario. A medida que aumenta el número de variables en combinación con funciones que manipulan estas variables, los beneficios se hacen más evidentes. Como último ejemplo:

$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

El lado izquierdo de la declaración sprintf expresa claramente la cadena y los tipos de valores esperados, mientras que el lado derecho expresa claramente las variables utilizadas y cómo se están manipulando.

Ro Mc
fuente
Estaba a punto de votar su respuesta por su primera oración excelente ( "sprintf es particularmente útil al formatear cadenas que usan números" ), que de hecho es el punto más importante de sprintfPHP, pero luego vi esos ejemplos intrincados que crean frutos de "falta" frutas :), cada una de las cuales solo muestra repetidamente %d(y abs(), lo que es irrelevante para el tema), en lugar de mostrar algunas de las muchas opciones de formato de números (que serían relevantes), cambié de opinión. Pero si actualiza la respuesta, todavía tendré mi voto a favor en la nevera. :) Gracias, saludos!
Sz.
3

Bueno, sprintf tiene muchas capacidades, ya que conocemos un ejemplo a continuación:

Hace unos meses necesitaba convertir segundos a formato hora: minuto: segundos Como $t = 494050 //secondsquería imprimir, 137 h 14 m 10 sasí que se me ocurrió la función php, springf()solo guardo los segundos $ty echo sprintf("%02d h %s%02d m %s%02d s", floor($t/3600), $f, ($t/60)%60, $f, $t%60);me da137 h 14 m 10 s

La función sprintf () es muy útil si sabemos cómo usarla.

Sahed
fuente
2

El argumento es el mismo para usar plantillas. Querrá separar su Textsleev de los valores de las variables reales. Además de los poderes adicionales de sprintf que mencionamos, es solo una cuestión de estilo.

worenga
fuente
2

Un caso de uso realmente bueno para usar sprintf es generar formatos de números rellenados y también, al mezclar diferentes tipos en una cadena. Puede ser más fácil de leer en muchos casos y hace que sea muy simple imprimir diferentes representaciones de la misma variable, especialmente las numéricas.

Lloyd Moore
fuente
2

El uso de la función sprintf () sobre la concatenación ordinaria tiene la ventaja de que puede aplicar diferentes tipos de formato en las variables a concatenar.

En tu caso, tienes

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

y

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

Tomemos $result = 'passed'; date = '23rd';

Usando la concatenación ordinaria solo puede obtener la salida:

Here is the result: passed for this date 23rd

Sin embargo, si lo usa sprintf(), puede obtener una salida modificada como:

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

Salida:

Here is the result: pass for this date 23

fuente
2

sprintf()es bastante similar a printf(). Si lo sabes printf()en detalle entonces sprintf()e incluso vsprintf()no es realmente difícil de entender.

una de las cosas que se diferencia sprintf()de printf()es que tendrá que declarar una variable para captar la salida de la función, ya que no se imprime directamente / echo nada. Veamos los siguientes fragmentos de código:

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"

Espero que ayude.

Vuong
fuente
1

Una "produce", la otra "devuelve", esa es una de las principales diferencias.

printf() Salidas

sprintf() Devoluciones

Mohd Abdul Mujib
fuente
0

Debes tener cuidado al usar sprintf en bucles:

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

Lo que conduce a los siguientes tiempos (en mi máquina local con PHP 7.2):

Lazos: 10000000

'$ a. $ b. $ c. $ d. $ e. $ f 'necesita 1.4507689476013s

"$ a $ b $ c $ d $ e $ f" necesita 1.9958319664001s

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

g3rdn
fuente
0

Lo uso para mensajes a los usuarios u otros tipos de funcionalidades "bonitas". Por ejemplo, si sé que voy a usar el nombre del usuario.

$name = 'Some dynamic name';

Y tener múltiples mensajes para usar en esa situación. (Es decir, bloquear o seguir a otro usuario)

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

Puede crear una función general que haga algo al usuario y agregar esta cadena, y no importa cuál sea la estructura de la oración, debería verse bastante bien. Siempre me disgusta simplemente agregar cadenas juntas y usar constantemente la notación de puntos y cerrar y volver a abrir cadenas solo para que una oración se vea bien. Al principio era un fanático como la mayoría, pero esto parece bastante útil cuando es necesario manipular varias cadenas y no desea codificar la ubicación de la variable en todo momento.

Piénsalo, ¿qué se ve mejor?

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

O

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

Claro que es un paso adicional, pero ¿qué pasa si sus comprobaciones condicionales hacen un montón de otras cosas funcionales, entonces las citas y los anexos comienzan a obstaculizar la codificación funcional?

Douglas Richardson
fuente