2010-09-29 5 views
11

PHP wordwrap() Funktion funktioniert nicht korrekt für Multi-Byte-Zeichenfolgen wie UTF-8.Multi-Byte-sichere wordwrap() -Funktion für UTF-8

Es gibt ein paar Beispiele für mb sichere Funktionen in den Kommentaren, aber mit einigen verschiedenen Testdaten scheinen sie alle einige Probleme zu haben.

Die Funktion sollte die gleichen Parameter wie wordwrap() nehmen.

Insbesondere sicher sein, es funktioniert auf:

  • Schnitt Mitte Wort, wenn $cut = true, nicht mitten im Wort nicht anders geschnitten
  • nicht zusätzliche Leerzeichen in Worten einfügen, wenn $break = ' '
  • arbeiten auch für $break = "\n"
  • Arbeit für ASCII, und alle gültigen UTF-8
+0

Die beiden Methoden [ 's ($ str) -> truncate ($ Länge, $ Pause)'] (https://github.com/delight-im/PHP-Str/blob/8fd0c608d5496d43adaa899642c1cce047e076dc/src /Str.php#L233) und ['s ($ str) -> truncateSafely ($ length, $ break)'] (https://github.com/delight-im/PHP-Str/blob/8fd0c608d5496d43adaa899642c1cce047e076dc/src/ Str.php # L246) tun genau das, wie in [diese Standalone-Bibliothek] (https://github.com/delight-im/PHP-Str) gefunden. Der erste ist für $ cut = true und der zweite für $ cut = false. Sie sind Unicode-sicher. – caw

Antwort

-2

Dieses gut zu funktionieren scheint ...

function mb_wordwrap($str, $width = 75, $break = "\n", $cut = false, $charset = null) { 
    if ($charset === null) $charset = mb_internal_encoding(); 

    $pieces = explode($break, $str); 
    $result = array(); 
    foreach ($pieces as $piece) { 
     $current = $piece; 
     while ($cut && mb_strlen($current) > $width) { 
     $result[] = mb_substr($current, 0, $width, $charset); 
     $current = mb_substr($current, $width, 2048, $charset); 
     } 
     $result[] = $current; 
    } 
    return implode($break, $result); 
} 
+0

sollte $ break nicht eher PHP_EOL sein? So wäre es plattformübergreifend? – ThatGuy

+1

mmm. Es teilt auch keine langen Wörter. – ThatGuy

+0

Warum explodieren Sie die Zeichenfolge Verwenden Sie Zeilenumbrüche? Sollten Sie nicht stattdessen Räume (zum Teilen von Wörtern) verwenden? –

-1

Hier ist meine eigene att Ich möchte auf eine Funktion verzichten, die ein paar meiner eigenen Tests bestanden hat, obwohl ich nicht versprechen kann, dass sie zu 100% perfekt ist, also poste bitte ein besseres, wenn du ein Problem siehst.

/** 
* Multi-byte safe version of wordwrap() 
* Seems to me like wordwrap() is only broken on UTF-8 strings when $cut = true 
* @return string 
*/ 
function wrap($str, $len = 75, $break = " ", $cut = true) { 
    $len = (int) $len; 

    if (empty($str)) 
     return ""; 

    $pattern = ""; 

    if ($cut) 
     $pattern = '/([^'.preg_quote($break).']{'.$len.'})/u'; 
    else 
     return wordwrap($str, $len, $break); 

    return preg_replace($pattern, "\${1}".$break, $str); 
} 
+0

'wordwrap()' umschließt nur ein Leerzeichen, wenn '$ cut'' false' ist. Aus diesem Grund funktioniert es für UTF-8, das abwärtskompatibel ist - Zeichen, die nicht in ASCII definiert sind, werden alle mit dem höchsten Bit-Satz codiert, wodurch eine Kollision mit ASCII-Zeichen einschließlich Leerzeichen verhindert wird. – Archimedix

+0

Können Sie klären? 'wordwrap()' funktioniert z. B. nicht für UTF-8. Ich bin mir nicht sicher, was du meinst "wickelt nur in einem Raum ein ..."" – philfreo

+1

Testen Sie Ihre Funktion auf dieser Zeichenfolge: проверка проверка – Yaroslav

5
/** 
* wordwrap for utf8 encoded strings 
* 
* @param string $str 
* @param integer $len 
* @param string $what 
* @return string 
* @author Milian Wolff <[email protected]> 
*/ 

function utf8_wordwrap($str, $width, $break, $cut = false) { 
    if (!$cut) { 
     $regexp = '#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){'.$width.',}\b#U'; 
    } else { 
     $regexp = '#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){'.$width.'}#'; 
    } 
    if (function_exists('mb_strlen')) { 
     $str_len = mb_strlen($str,'UTF-8'); 
    } else { 
     $str_len = preg_match_all('/[\x00-\x7F\xC0-\xFD]/', $str, $var_empty); 
    } 
    $while_what = ceil($str_len/$width); 
    $i = 1; 
    $return = ''; 
    while ($i < $while_what) { 
     preg_match($regexp, $str,$matches); 
     $string = $matches[0]; 
     $return .= $string.$break; 
     $str = substr($str, strlen($string)); 
     $i++; 
    } 
    return $return.$str; 
} 

Gesamtzeit: 0,0020880699 ist gute Zeit :)

+0

Wenn nicht '$ cut', hat diese Funktion einen Fehler. Es wird nicht früher, wenn möglich, wickelt (das, was [ 'wordwrap'] (http://php.net/wordwrap) tun würde. [Siehe Demo] (http://codepad.viper-7.com/zxE64Z. php53_t) Nicht eine Lösung, aber eine verwandte Antwort hat eine andere [Wordwrap Regex] (http://stackoverflow.com/q/2682861/367456#2689242). – hakre

+0

Dieses Verhalten anders als 'wordwrap()', in Bezug auf Leerzeichen. – mpyw

+0

Diese eine Arbeit, wenn cut = true auf Chinesisch vereinfachtem Text – exiang

14

Ich habe keine funktionierende Code für mich gefunden. Hier ist was ich geschrieben habe. Für mich funktioniert es, dachte es ist wahrscheinlich nicht das schnellste.

+4

Dies ist die eine, die richtig funktioniert – Danack

+0

Arbeitete gut für mich auch! –

+0

Ich habe dies seit ein paar Jahren, aber nicht schwer.Allerdings habe ich diese Funktion in einem PHP-Klasse Ich legte als ein Git auf Github unter MIT und muss nur überprüfen, ob das in Ordnung ist - https://gist.github.com/AliceWonderMiscreations/7694e8aa644cf1b1fc3910b1c949e092 –

0

Hier ist die Multibyte-Wordwrap-Funktion, die ich habe inspiriert von anderen im Internet gefunden.

function mb_wordwrap($long_str, $width = 75, $break = "\n", $cut = false) { 
    $long_str = html_entity_decode($long_str, ENT_COMPAT, 'UTF-8'); 
    $width -= mb_strlen($break); 
    if ($cut) { 
     $short_str = mb_substr($long_str, 0, $width); 
     $short_str = trim($short_str); 
    } 
    else { 
     $short_str = preg_replace('/^(.{1,'.$width.'})(?:\s.*|$)/', '$1', $long_str); 
     if (mb_strlen($short_str) > $width) { 
      $short_str = mb_substr($short_str, 0, $width); 
     } 
    } 
    if (mb_strlen($long_str) != mb_strlen($short_str)) { 
     $short_str .= $break; 
    } 
    return $short_str; 
} 

Nicht‘vergessen PHP für die Verwendung von UTF-8 mit konfigurieren:

ini_set('default_charset', 'UTF-8'); 
mb_internal_encoding('UTF-8'); 
mb_regex_encoding('UTF-8'); 

ich, das wird helfen hoffen. Guillaume

1

Weil keine Antwort jeden Anwendungsfall behandelt, hier ist etwas, das tut. Der Code basiert auf Drupal’s AbstractStringWrapper::wordWrap.

<?php 

/** 
* Wraps any string to a given number of characters. 
* 
* This implementation is multi-byte aware and relies on {@link 
* http://www.php.net/manual/en/book.mbstring.php PHP's multibyte 
* string extension}. 
* 
* @see wordwrap() 
* @link https://api.drupal.org/api/drupal/core%21vendor%21zendframework%21zend-stdlib%21Zend%21Stdlib%21StringWrapper%21AbstractStringWrapper.php/function/AbstractStringWrapper%3A%3AwordWrap/8 
* @param string $string 
* The input string. 
* @param int $width [optional] 
* The number of characters at which <var>$string</var> will be 
* wrapped. Defaults to <code>75</code>. 
* @param string $break [optional] 
* The line is broken using the optional break parameter. Defaults 
* to <code>"\n"</code>. 
* @param boolean $cut [optional] 
* If the <var>$cut</var> is set to <code>TRUE</code>, the string is 
* always wrapped at or before the specified <var>$width</var>. So if 
* you have a word that is larger than the given <var>$width</var>, it 
* is broken apart. Defaults to <code>FALSE</code>. 
* @return string 
* Returns the given <var>$string</var> wrapped at the specified 
* <var>$width</var>. 
*/ 
function mb_wordwrap($string, $width = 75, $break = "\n", $cut = false) { 
    $string = (string) $string; 
    if ($string === '') { 
    return ''; 
    } 

    $break = (string) $break; 
    if ($break === '') { 
    trigger_error('Break string cannot be empty', E_USER_ERROR); 
    } 

    $width = (int) $width; 
    if ($width === 0 && $cut) { 
    trigger_error('Cannot force cut when width is zero', E_USER_ERROR); 
    } 

    if (strlen($string) === mb_strlen($string)) { 
    return wordwrap($string, $width, $break, $cut); 
    } 

    $stringWidth = mb_strlen($string); 
    $breakWidth = mb_strlen($break); 

    $result = ''; 
    $lastStart = $lastSpace = 0; 

    for ($current = 0; $current < $stringWidth; $current++) { 
    $char = mb_substr($string, $current, 1); 

    $possibleBreak = $char; 
    if ($breakWidth !== 1) { 
     $possibleBreak = mb_substr($string, $current, $breakWidth); 
    } 

    if ($possibleBreak === $break) { 
     $result .= mb_substr($string, $lastStart, $current - $lastStart + $breakWidth); 
     $current += $breakWidth - 1; 
     $lastStart = $lastSpace = $current + 1; 
     continue; 
    } 

    if ($char === ' ') { 
     if ($current - $lastStart >= $width) { 
     $result .= mb_substr($string, $lastStart, $current - $lastStart) . $break; 
     $lastStart = $current + 1; 
     } 

     $lastSpace = $current; 
     continue; 
    } 

    if ($current - $lastStart >= $width && $cut && $lastStart >= $lastSpace) { 
     $result .= mb_substr($string, $lastStart, $current - $lastStart) . $break; 
     $lastStart = $lastSpace = $current; 
     continue; 
    } 

    if ($current - $lastStart >= $width && $lastStart < $lastSpace) { 
     $result .= mb_substr($string, $lastStart, $lastSpace - $lastStart) . $break; 
     $lastStart = $lastSpace = $lastSpace + 1; 
     continue; 
    } 
    } 

    if ($lastStart !== $current) { 
    $result .= mb_substr($string, $lastStart, $current - $lastStart); 
    } 

    return $result; 
} 

?>