26

像 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() 像这样:%-12s %-8s -%-10s -%20s %8.2f

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

4

4 回答 4

13

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

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

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

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

于 2013-04-14T20:27:35.227 回答
11

我实际上是想知道 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}s\n", $text);
printf("%{$mbTargetLength}s\n", $mbText);
printf("%{$mbRussianTargetLength}s\n", $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 %10s\n", 'thüs', 'wörks', 'ök');
echo mb_sprintf("%-10s %-10s %10s\n", 'this', 'works', 'ok');

结果

thüs       wörks              ök
this       works              ok

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

于 2019-04-30T19:02:38.510 回答
4

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

utf8_encode(sprintf("%-12s %-8s", utf8_decode($paramOne), utf8_decode($paramTwo))
于 2018-09-26T07:48:20.873 回答
0

问题

没有多字节格式功能。

主意

您无法转换输入字符串。您应该更改格式长度。格式%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并且输入是一个亚洲字符,您应该需要两个空格(填充)而不是三个。

于 2021-04-17T15:00:14.567 回答