Навіщо використовувати функцію sprintf в PHP?


188

Я намагаюся дізнатися більше про функцію 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;

Я щось тут пропускаю?


40
+1, гарне запитання. Я дуже рідко використовую це сам, тому мені цікаво побачити відповіді.
зомбат

6
Зауважте, що ваші два приклади не рівнозначні: перший просто призначає результат $output, а другий - echoце.
Даніель Приден

Я особисто використовую це, коли я об'єдную багато речей. Лише тоді я вважаю це корисним, головним чином для читабельності.
AlexMorley-Finch

Відповіді:


130

sprintf має всі можливості форматування оригінального printf, що означає, що ви можете зробити набагато більше, ніж просто вставити змінні значення в рядки.

Наприклад, вкажіть формат чисел (шістнадцятковий, десятковий, восьмеричний), кількість десяткових знаків, прокладки тощо. Google для printf, і ви знайдете безліч прикладів. Стаття з вікіпедії на printf повинна розпочати.


46
Роки тому я написав приємну функцію для нульових цифр. Використовувався віками, перш ніж усвідомити, що я міг просто зробитиsprintf('%03d', $num)
DisgruntledGoat

5
PHP sprintf має всі можливості форматування ... насправді відсутній важливий, %,8dнаприклад, щоб отримати правильно вирівняне 8-розрядне чи комедо-розділене ціле число. Використовувати number_formatменш зручно в printfстилі.
e2-e4

3
@DisgruntledGoat Чи можете ви зламати цей рядок форматування ('% 03d')? Я думаю, це було б дуже корисно. Дякую.
Містер Смі

3
Ви також можете повторити та упорядкувати аргументи для відображення. напр., в sprintf('%2$s %1$s, my name is also %1$s.', 'World', 'Hello');результатіHello World, my name is also World.
фіри

77

Існує багато випадків використання для sprintf, але один із способів, яким я їх використовую, - це збереження рядка таким чином: "Привіт, моє ім'я% s" в базі даних або як константа в класі PHP. Таким чином, коли я хочу використовувати цей рядок, я можу просто зробити це:

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

По суті це дозволяє деякий поділ у коді. Якщо я використовую "Здрастуйте, моє ім'я% s" у багатьох місцях свого коду, я можу змінити його на "% s - це моє ім'я" в одному місці, і воно оновлюється скрізь в іншому місці автоматично, без необхідності переходити до кожного примірника і рухатися конкатенації.


8
Це. Тепер додайте нумеровані аргументи та переклади:]
Конерак

46

Ще одне використання 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 для зберігання локалізованих рядків, але ви отримуєте ідею.



37

Простіше перекласти.

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
Я бачу, де це було б дуже корисно +1
JasonDavis

6
Хоча для перекладів, мабуть, було б краще використовувати специфікатори позицій, щоб дозволити додаткову гнучкість при перекладі, тобто: 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
PatrikAkerstrand

1
Хоча навіть якщо їх немає в початковому рядку, ви можете помістити їх у переклад, і вони спрацюють.
спектри

17

Найкраща причина, яку я знайшов, це те, що вона дозволяє розміщувати всі мовні рядки у мовному файлі, якщо люди можуть перекладати та замовляти їх за потребою - але ви все ще знаєте, що незалежно від того, у якому форматі знаходиться рядок - ви хочете показати ім'я користувачів.

Наприклад, на вашому веб-сайті у верхній частині сторінки буде написано "Привітання назад [[Користувача]]". Як програміст ви не знаєте і не цікавитесь як хлопці з інтерфейсу будуть писати це - ви просто знаєте, що ім’я користувачів буде показано десь у повідомленні.

Таким чином, ви можете вбудувати повідомлення у свій код, не турбуючись про те, що таке повідомлення насправді.

Файл мови (EN_US):

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

Тоді ви можете підтримати будь-який тип повідомлення будь-якою мовою, використовуючи це у вашому фактичному PHP-коді.

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

Мені ніколи не доводилося турбуватися про те, щоб робити мовні файли, але це здається одним із найкорисніших для мене застосувань
JasonDavis,

Цей приклад також мав ідеальний сенс для мене, оскільки я не робив багато багатоязикових програм.
Веб-рішення MKN

9

чому б ви хотіли ним користуватися?

Це виявляється дуже корисним при використанні (зовнішнього) джерела для мовних рядків. Якщо вам потрібна фіксована кількість змінних у певній багатомовній рядку, вам потрібно знати лише правильне впорядкування:

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, таким чином, це також однофайл для форматування численних типів змінних, наприклад:


8

Як було сказано, це дозволяє форматувати вхідні дані. Наприклад, примусові 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. Якщо проаналізувати несанітизований вхід, ваш сайт і сервер будуть зламані . (За винятком, звичайно, змінних, які повністю побудовані вашим кодом і гарантовано є безпечними.)


2
Цей код вразливий до ін'єкції SQL. Використовуйте заповнювачі, а не запитуйте інтерполяцію!
duskwuff -inactive-

@duskwuff - Використовується так, як є, ти маєш рацію. Вибачте, я змінив свою відповідь.
Самуель Йешке

7

Використання 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(), але він видає відформатовану рядок, а не повертає її (до змінної).

Що читає?


1
Я не погоджуюся, але також варто згадати, що я запитав це 6-7 років тому і, думаючи, я не можу згадати один раз, що я використовував це з тих пір! Я сподіваюся, що колись знайду для нього місце, і я вірю, що в ньому є місце, але смішно думати, що я не використовував його, оскільки задав це питання багато років тому!
JasonDavis

Я можу сказати, що це дещо схоже на встановлення змінних для PDO в PHP. Крім того, якщо ви хочете надати йому аргумент читабельності, то в такому випадку я вважаю за краще використовувати sprintf()розбитий на кілька рядків, де рядок із заповнювачами знаходиться на 1 рядку, а значення знаходяться на рядку або навіть декількох рядках під ним, усі з відступом правильно звичайно. Використання вашої демонстрації так, як я описав ... i.imgur.com/DFi3ur8.png
JasonDavis

Підготовлені заяви у поєднанні із збереженими процедурами є найкращим способом запобігання введення sql, а не sprintf. Ви все ще можете легко ввести sql за допомогою sprintf, якщо рядок містить одну цитату
HumbleWebDev

4

Навіть я хоч те саме, якщо я не використовував його недавно. Коли ви генеруєте документи на основі даних користувача, це стане в нагоді.

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

Який можна легко записати як

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

Або ви можете зробити це: "<p>Some big paragraph {$a['name']} more words {$a['age']}</p>". ТАКОЖ, НЕ ЗАБУДУЙТЕ санітувати введення користувачів або відкривати код до вразливостей XSS.
thirdender

Ви також можете використовувати синтаксис HEREDOC . На мою думку, обидва легше читати, ніж синтаксис sprintf.
thirdender

4

Це:

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

На мій погляд, це зрозуміліше для читання, але я бачу використання для локалізації чи форматування.


3

Деякі з типових випадків, коли вам потрібен більш точний контроль над вихідним форматом. Це може бути складним, наприклад, переконатися, що певне значення має конкретну кількість пробілів, зафіксованих спереду, залежно від його довжини, або що число виводиться у конкретному точному форматі.

Прикладів у посібнику з PHP дуже багато

Також те, що стосується вашого прикладу "простіше писати". Хоча ехо може бути простіше написати, спринт легше читати, особливо якщо у вас багато змінних.

Ще однією причиною використання sprintf або printf може бути те, що ви хочете дозволити користувачу визначити вихідний формат деякого значення - ви можете сміливо дозволити їм визначити формат виводу, сумісний з sprintf.

О, і ваш приклад насправді неправильний для однієї частини. sprintfповертає рядок, але echoне робить - echoодразу виводить його і нічого не повертає, а sprintfпросто повертає.


3
  1. Якщо ви використовували C / C ++, то ви використовували б функцію sprintf.

  2. Є хороший шанс, що другий рядок буде менш ефективним. Echo розроблений як команда виводу, тоді як sprintf призначений для підстановки рядкових токенів. Я не людина PHP, але я підозрюю, що в ехо є більше об'єктів. Якщо вона діє так, як Java, вона створює нову рядок щоразу, коли щось додається до списку, тож у результаті ви створите 4 рядки.


Останні видання Java перетворять об'єднання рядків у StringBuilder поза кадром.
RodeoClown

1
Рядки в PHP не є об'єктами. Другий приклад використовує мовну конструкцію замість виклику функції, тому буде непомітно швидше.
Тамлін

3

Іноді мені трапляється щось подібне, що я вважаю трохи легшим для читання:

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

3

Зазвичай я використовую sprintf, щоб переконатися, що ідентифікатор, що надходить від введення користувача, є цілим числом, наприклад:

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

Для цього типу коду краще використовувати вбудований mysql_real_escape_string (): $ query = sprintf ("SELECT * зі статей, де 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");

3

sprintf особливо корисний при форматуванні рядків, що використовують числа. Наприклад,

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

Output: There are -2 oranges in the basket

Апельсини відформатовані як ціле число (-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, але потім бачу ті заплутані приклади, що створюють плоди з "пропущених" фрукти :), кожен раз лише показуючи %dabs()це не має значення для теми), замість того, щоб показувати кілька варіантів форматування чисел (що було б актуально), я передумав. Але якщо ви оновите відповідь, я все одно отримаю свою заяву в холодильнику для вас. :) Thx, ура!
Sz.

3

Ну, 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 () дуже корисна, якщо ми знаємо, як її використовувати.


2

Аргумент той самий, що і для використання шаблонів. Ви хочете відокремити свій Textsleev від фактичних змінних значень. Крім додаткових повноважень sprintf, про які ми згадуємо, це просто річ у стилі.


2

Дійсно сприятливим випадком використання sprintf є виведення прокладених форматів чисел, а також при змішуванні різних типів у рядку. У багатьох випадках його можна легше читати, а також надрукувати друк різних зображень однієї змінної, особливо числових, дуже просто.


2

Використання функції 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

2

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"

Сподіваюся, що це допомагає.



0

Ви повинні бути обережними при використанні 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. $ б. $ c. $ d. $ е. $ 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


0

Я використовую його для повідомлень користувачам або інших "гарних" типів функціональності. Наприклад, якщо я знаю, що я буду використовувати ім'я користувача.

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

Звичайно, це зайвий крок, але що робити, якщо ваші умовні перевірки роблять купу інших функціональних речей, тоді цитати та додавання починають перешкоджати функціональному кодуванню.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.