【问题标题】:php sprintf() with foreign characters?带有外来字符的php sprintf()?
【发布时间】:2013-04-06 21:04:43
【问题描述】:

像 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

【问题讨论】:

  • 这个问题(不同的字符由不同的字节数组成,不同的字素簇由不同的字符数组成)有点类似于(但不一样)@ 987654321@。最重要的是,将数据放在 HTML 表格中可能是最简单的方法。
  • 是的,这绝对不是重复的,这个问题是关于多字节字符的 sprintf(),另一个是关于字体显示宽度的问题。
  • 这根本不是一个重复的问题...您可以这样做: utf8_encode(sprintf('format', utf8_decode($yourstring));... 当然你会如果给出了很多参数,则必须检查每个参数。
  • 这个问题是关于 unicode 代码点高于 127 的字符,当使用 UTF-8 编码时使用超过一个字节。不幸的是 sprintfprintf 不处理这个问题。当打印使用 UTF-8 编码时使用 6 个字节的 2 字符串时,%8s 打印错误的空格数 (8-6=2) 而不是 (8-2=6)。这 NOTHING 与所使用的字体有关,就像这个问题应该重复的问题一样。这个问题是关于 phps 缺乏对多字节字符的支持。

标签: php utf-8 printf multibyte-functions


【解决方案1】:

问题

没有多字节格式功能。

想法

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 正确的解释,但链接的函数对我不起作用——即使在做了备注中提到的 mb_* 函数名替换之后。我希望有一个比 @nimmneun 提供的更好的解决方案,这也是我目前的 hacky 解决方案。
    【解决方案3】:

    我实际上是想知道 PHP ^7 是否终于有了原生的 mb_sprintf() 但显然没有 xD。

    为了完整起见,这是我在一些旧项目中一直使用的简单解决方案。它只是将strlenmb_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 年 6 月 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 将被忽略/保持不变。

    【讨论】:

    • 我曾希望找到一些不那么老套的东西,因为这也是我一直在做的事情 - 因为@Martin Prikryl 中的链接例程不起作用(对我来说),所以被赞成。跨度>
    • 你让我再给它一次 =)
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-30
      • 2017-06-07
      • 1970-01-01
      • 2014-01-13
      • 1970-01-01
      • 1970-01-01
      • 2012-05-13
      • 1970-01-01
      相关资源
      最近更新 更多