带有外部字符的PHP sprintf()



接缝像sprintf有外文字符的问题?还是我做错了什么?当从字符串中删除åäö等字符时,看起来它可以工作。这有必要吗?

我希望以下行在报告中正确对齐:

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

我使用sprintf()是这样的: % -10年代-12年代% 8 s - % - % 20 s % 8.2 f

使用:php-5.3.23-nts-Win32-VC9-x86

PHP中的字符串基本上是字节数组(而不是字符)。它们不能原生地使用多字节编码(如UTF-8)。

详情见:
https://www.php.net/manual/en/language.types.string.php language.types.string.details

PHP中的大多数字符串函数都有多字节的等效函数(带有mb_前缀)。但是sprintf没有。

有一个用户评论(由"viktor at textalk。com")在php.net的函数文档页面上实现了sprintf的多字节实现。它可能对你有用:
89020年https://www.php.net/manual/en/function.sprintf.php

我实际上是试图找出如果PHP ^7最终有一个本地mb_sprintf(),但显然没有xD。

为了完整起见,这里是我在一些老项目中使用的一个简单的解决方案。它只是增加了strlen &mb_strlen到所需的$targetLengh。添加非多字节示例只是为了便于比较=)。

$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}sn", $text);
printf("%{$mbTargetLength}sn", $mbText);
printf("%{$mbRussianTargetLength}sn", $mbTextRussian);
结果

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

更新2019-06-12


@flowtron让我重新思考。一个简单的mb_sprintf()可以是这样的:

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 %10sn", 'thüs', 'wörks', 'ök');
echo mb_sprintf("%-10s %-10s %10sn", 'this', 'works', 'ok');
结果

thüs       wörks              ök
this       works              ok

我在这里只做了一些愉快的路径测试,但它适用于PHP>=5.6,并且应该足以让ppl了解如何封装该行为。但它不适用于重复/顺序修饰符,例如%1$20s将被忽略/保持不变。

如果您正在使用适合ISO-8859-1字符集的字符,您可以在格式化之前转换字符串,并在完成后将结果转换回UTF8

utf8_encode(sprintf("%-12s %-8s", utf8_decode($paramOne), utf8_decode($paramTwo))

问题

没有多字节格式函数。

<<h3>想法/h3>

不能转换输入字符串。您应该更改格式长度。格式%4s表示4个宽度(不是字符 -参见脚注)。但是PHP格式函数计数字节。所以你应该给bytes - widths添加格式长度。

实现从@nimmneun

function mb_sprintf($format, ...$args) {
    $params = $args;
    $callback = function ($length) use (&$params) {
        $value = array_shift($params);
        return $length[0] + strlen($value) - mb_strwidth($value);
    };
    $format = preg_replace_callback('/(?<=%|%-)d+(?=s)/', $callback, $format);
    return sprintf($format, ...$args);
}

还有一个选项str_pad($input, $length, $pad_char=' ', STR_PAD_RIGHT)

function mb_str_pad(...$args) {
    $args[1] += strlen($args[0]) - mb_strwidth($args[0]);
    return str_pad(...$args);
}

脚注

亚洲字符有3个字节、2个宽度和1个字符长度。如果您的格式是%4s,并且输入是一个亚洲字符,则应该需要两个空格(填充),而不是三个。

最新更新