Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

php sprintf() with foreign characters?

Seams to be like sprintf have a problem with foregin characters? Or is it me doing something wrong? Looks like it work when removing chars like åäö from the string though. Should that be necessary?

I want the following lines to be aligned correctly for a report:

2011-11-27   A1823    -Ref. Leif  -           12 873,00    18.98
2011-11-30   A1856    -Rättat xx -            6 594,00    19.18

I'm using sprintf() like this: %-12s %-8s -%-10s -%20s %8.2f

Using: php-5.3.23-nts-Win32-VC9-x86

like image 294
Mille Avatar asked Apr 14 '13 19:04

Mille


People also ask

Is the use of sprintf () function in PHP?

The sprintf() function writes a formatted string to a variable. The arg1, arg2, ++ parameters will be inserted at percent (%) signs in the main string. This function works "step-by-step". At the first % sign, arg1 is inserted, at the second % sign, arg2 is inserted, etc.

What does %s mean in PHP?

%s is a type specifier which will be replaced to valuable's value (string) in case of %s . Besides %s you can use other specifiers, most popular are below: d - the argument is treated as an integer, and presented as a (signed) decimal number.

Is Sprintf PHP safe?

Not in any traditional sense, as PHP's sprintf doesn't support any of the really dangerous conversions like %n . A user-controlled format string can still cause some limited havoc (consider %99999999s ), but about the worst I think it could do would be to consume memory and time. Save this answer.


3 Answers

Strings in PHP are basically arrays of bytes (not characters). They cannot work natively with multibyte encodings (such as UTF-8).

For details see:
https://www.php.net/manual/en/language.types.string.php#language.types.string.details

Most string functions in PHP have multibyte equivalent though (with the mb_ prefix). But the sprintf does not.

There's a user comment (by "viktor at textalk dot com") with multibyte implementation of the sprintf on the function's documentation page at php.net. It may work for you:
https://www.php.net/manual/en/function.sprintf.php#89020

like image 148
Martin Prikryl Avatar answered Nov 09 '22 14:11

Martin Prikryl


I was actually trying to find out if PHP ^7 finally has a native mb_sprintf() but apparently no xD.

For the sake of completeness, here is a simple solution I've been using in some old projects. It just adds the diff between strlen & mb_strlen to the desired $targetLengh. The non-multibyte example is just added for the sake of easy comparison =).

$text = "Gultigkeitsprufung ist fehlgeschlagen: %{errors}";
$mbText = "Gültigkeitsprüfung ist fehlgeschlagen: %{errors}";
$mbTextRussian = "Проверка не удалась: %{errors}";

$targetLength = 60;
$mbTargetLength = strlen($mbText) - mb_strlen($mbText) + $targetLength;
$mbRussianTargetLength = strlen($mbTextRussian) - mb_strlen($mbTextRussian) + $targetLength;

printf("%{$targetLength}s\n", $text);
printf("%{$mbTargetLength}s\n", $mbText);
printf("%{$mbRussianTargetLength}s\n", $mbTextRussian);

result

            Gultigkeitsprufung ist fehlgeschlagen: %{errors}
            Gültigkeitsprüfung ist fehlgeschlagen: %{errors}
                              Проверка не удалась: %{errors}

update 2019-06-12


@flowtron made me give it another thought. A simple mb_sprintf() could look like this.

function mb_sprintf($format, ...$args) {
    $params = $args;

    $callback = function ($length) use (&$params) {
        $value = array_shift($params);
        return strlen($value) - mb_strlen($value) + $length[0];
    };

    $format = preg_replace_callback('/(?<=%|%-)\d+(?=s)/', $callback, $format);

    return sprintf($format, ...$args);
}

echo mb_sprintf("%-10s %-10s %10s\n", 'thüs', 'wörks', 'ök');
echo mb_sprintf("%-10s %-10s %10s\n", 'this', 'works', 'ok');

result

thüs       wörks              ök
this       works              ok

I only did some happy path testing here, but it works for PHP >=5.6 and should be good enough to give ppl an idea on how to encapsulate the behavior. It does not work with the repetition/order modifiers though - e.g. %1$20s will be ignored/remain unchanged.

like image 36
nimmneun Avatar answered Nov 09 '22 13:11

nimmneun


If you're using characters that fit in the ISO-8859-1 character set, you can convert the strings before formatting, and convert the result back to UTF8 when you are done

utf8_encode(sprintf("%-12s %-8s", utf8_decode($paramOne), utf8_decode($paramTwo))
like image 40
Vestman Avatar answered Nov 09 '22 13:11

Vestman