php sprintf() mit fremden Zeichen?

Lesezeit: 5 Minuten

Benutzer-Avatar
Mille

Scheint so zu sein, als ob sprintf ein Problem mit fremden Zeichen hat? Oder mache ich etwas falsch? Sieht so aus, als ob es funktioniert, wenn Zeichen wie åäö aus der Zeichenfolge entfernt werden. Sollte das nötig sein?

Ich möchte, dass die folgenden Zeilen für einen Bericht korrekt ausgerichtet werden:

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

Ich verwende sprintf() wie folgt: %-12s %-8s -%-10s -%20s %8.2f

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

  • Dieses Problem (dass unterschiedliche Zeichen aus einer unterschiedlichen Anzahl von Bytes bestehen und unterschiedliche Graphem-Cluster aus einer unterschiedlichen Anzahl von Zeichen bestehen) ist etwas ähnlich (aber nicht dasselbe wie) stackoverflow.com/questions/9166698/…. Unter dem Strich ist es möglicherweise am einfachsten, die Daten stattdessen in eine HTML-Tabelle zu packen.

    – Bitte stehen

    14. April 2013 um 20:24 Uhr

  • Ja, das ist definitiv kein Duplikat, diese Frage bezieht sich auf Multibyte-Zeichen ist sprintf (), die andere bezieht sich auf die Breite der Schriftanzeige.

    – Xyphoid

    29. August 2013 um 23:26 Uhr

  • Dies war überhaupt keine doppelte Frage … Sie können den Trick ausführen, indem Sie Folgendes tun: utf8_encode (sprintf (‘format’, utf8_decode ($ yourstring)); … Natürlich müssen Sie alle Argumente überprüfen, wenn viele vorhanden sind gegeben.

    – Gérald Croës

    25. September 2013 um 13:57 Uhr


  • Bei dieser Frage geht es um Zeichen mit einem Unicode-Codepunkt über 127, die bei der Codierung mit UTF-8 mehr als ein Byte verwenden. Leider sprintf und printf handhabe das nicht. Beim Drucken einer Zeichenfolge mit 2 Zeichen, die bei Codierung mit UTF-8 6 Bytes verwendet, %8s druckt die falsche Anzahl von Leerzeichen (8-6=2) statt (8-2=6). Das hat NICHTS mit der verwendeten Schriftart zu tun haben, wie die Frage, von der diese Frage dupliziert werden soll. Diese Frage bezieht sich auf die fehlende Unterstützung von phps für Multibyte-Zeichen.

    – etwas

    17. Januar 2014 um 23:28 Uhr

Benutzer-Avatar
Martin Prikryl

Strings in PHP sind im Grunde Arrays von Bytes (keine Zeichen). Sie können nicht nativ mit Multibyte-Codierungen (wie UTF-8) arbeiten.

Einzelheiten siehe:
https://www.php.net/manual/en/language.types.string.php#language.types.string.details

Die meisten String-Funktionen in PHP haben jedoch ein Multibyte-Äquivalent (mit der mb_ Präfix). Aber die sprintf nicht.

Es gibt einen Benutzerkommentar (von “viktor at textalk dot com”) mit Multibyte-Implementierung der sprintf auf der Dokumentationsseite der Funktion auf php.net. Es kann für Sie funktionieren:
https://www.php.net/manual/en/function.sprintf.php#89020

  • richtige Erklärung, aber die verlinkte Funktion funktioniert bei mir nicht – auch nach dem Ersetzen des mb_*-Funktionsnamens, der in den Bemerkungen erwähnt wird. Ich hatte auf eine bessere Lösung gehofft, als @nimmneun bereitgestellt hat, es ist auch meine aktuelle Hacky-Lösung.

    – Flowtron

    12. Juni 2019 um 14:37 Uhr

Benutzer-Avatar
nimmneun

Ich habe eigentlich versucht herauszufinden, ob PHP ^7 endlich einen nativen hat mb_sprintf() aber anscheinend nein xD.

Der Vollständigkeit halber hier eine einfache Lösung, die ich in einigen alten Projekten verwendet habe. Es fügt nur den Unterschied zwischen hinzu strlen & mb_strlen zum gewünschten $targetLengh. Das Nicht-Multibyte-Beispiel wurde nur zum einfachen Vergleich hinzugefügt =).

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

Ergebnis

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

Update 12.06.2019


@flowtron hat mich dazu gebracht, noch einmal darüber nachzudenken. Eine einfache mb_sprintf() könnte so aussehen.

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

Ergebnis

thüs       wörks              ök
this       works              ok

Ich habe hier nur ein paar Happy-Path-Tests durchgeführt, aber es funktioniert für PHP >=5.6 und sollte gut genug sein, um ppl eine Vorstellung davon zu geben, wie man das Verhalten kapselt. Es funktioniert jedoch nicht mit den Wiederholungs-/Reihenfolge-Modifikatoren – zB %1$20s werden ignoriert/bleiben unverändert.

  • Ich hatte gehofft, etwas weniger Hackiges zu finden, weil ich es auch so gemacht habe – positiv bewertet, da die verlinkte Routine in @Martin Prikryl (für mich) nicht funktioniert.

    – Flowtron

    12. Juni 2019 um 14:38 Uhr

  • du hast mich dazu gebracht, es noch einmal zu geben =)

    – nimmneun

    12. Juni 2019 um 20:54 Uhr

Wenn Sie Zeichen verwenden, die in den ISO-8859-1-Zeichensatz passen, können Sie die Zeichenfolgen vor dem Formatieren konvertieren und das Ergebnis nach Abschluss wieder in UTF8 konvertieren

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

Problem

Es gibt keine Multibyte-Formatfunktionen.

Idee

Eingabezeichenfolgen können nicht konvertiert werden. Sie sollten Formatlängen ändern. Ein Format %4s bedeutet 4 Breiten (nicht Figuren – siehe Fußnote). Aber PHP-Formatfunktionen zählen Byte. Sie sollten also Formatlängen hinzufügen bytes - widths.

Implementierungen

von @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);
}

Und vergessen Sie nicht eine andere Option 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);
}

Fußnote

Asiatische Zeichen haben 3 Bytes und 2 Breite und 1 Zeichenlänge. Wenn Ihr Format ist %4s und die Eingabe ein asiatisches Zeichen ist, sollten Sie zwei Leerzeichen (Padding) und nicht drei benötigen.

1055460cookie-checkphp sprintf() mit fremden Zeichen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy