【问题标题】:How to reverse a Unicode string如何反转 Unicode 字符串
【发布时间】:2010-09-30 20:38:03
【问题描述】:

comment to an answer to this question 中暗示 PHP 不能反转 Unicode 字符串。

对于 Unicode,它适用于 PHP 因为大多数应用程序将其处理为 二进制。是的,PHP 是 8 位干净的。尝试 PHP中的等价物:perl -Mutf8 -e 'print scalar reverse("ほげほげ")' 你会得到垃圾, 不是“げほげほ”。 – jrockway

不幸的是,PHP 的 unicode 支持 atm 充其量“缺乏”是正确的。这将hopefully change drastically with PHP6

PHPs MultiByte functions 确实提供了处理 unicode 所需的基本功能,但它不一致并且缺少很多功能。其中之一是反转字符串的函数。

我当然想把这段文字颠倒一下,然后看看是否有可能。我做了一个函数来完成反转这个 Unicode 文本的巨大复杂任务,所以在 PHP6 之前你可以放松一点。

测试代码:

$enc = 'UTF-8';
$text = "ほげほげ";
$defaultEnc = mb_internal_encoding();

echo "Showing results with encoding $defaultEnc.\n\n";

$revNormal = strrev($text);
$revInt = mb_strrev($text);
$revEnc = mb_strrev($text, $enc);

echo "Original text is: $text .\n";
echo "Normal strrev output: " . $revNormal . ".\n";
echo "mb_strrev without encoding output: $revInt.\n";
echo "mb_strrev with encoding $enc output: $revEnc.\n";

if (mb_internal_encoding($enc)) {
    echo "\nSetting internal encoding to $enc from $defaultEnc.\n\n";

    $revNormal = strrev($text);
    $revInt = mb_strrev($text);
    $revEnc = mb_strrev($text, $enc);

    echo "Original text is: $text .\n";
    echo "Normal strrev output: " . $revNormal . ".\n";
    echo "mb_strrev without encoding output: $revInt.\n";
    echo "mb_strrev with encoding $enc output: $revEnc.\n";

} else {
    echo "\nCould not set internal encoding to $enc!\n";
}

【问题讨论】:

    标签: php string unicode reverse


    【解决方案1】:

    答案

    function mb_strrev($text, $encoding = null)
    {
        $funcParams = array($text);
        if ($encoding !== null)
            $funcParams[] = $encoding;
        $length = call_user_func_array('mb_strlen', $funcParams);
    
        $output = '';
        $funcParams = array($text, $length, 1);
        if ($encoding !== null)
            $funcParams[] = $encoding;
        while ($funcParams[1]--) {
             $output .= call_user_func_array('mb_substr', $funcParams);
        }
        return $output;
    }
    

    【讨论】:

      【解决方案2】:

      这是另一种方式。这似乎无需指定输出编码即可工作(用几个不同的mb_internal_encodings 测试):

      function mb_strrev($text)
      {
          return join('', array_reverse(
              preg_split('~~u', $text, -1, PREG_SPLIT_NO_EMPTY)
          ));
      }

      【讨论】:

      • 为此 +1。它让我想起了在 Perl 中的做法:my $reversed = join '', reverse split /(\X)/, $original;
      【解决方案3】:

      这是另一种使用正则表达式的方法:

      function utf8_strrev($str){
       preg_match_all('/./us', $str, $ar);
       return implode(array_reverse($ar[0]));
      }
      

      【讨论】:

        【解决方案4】:

        另一种方法:

        function mb_strrev($str, $enc = null) {
            if(is_null($enc)) $enc = mb_internal_encoding();
            $str = mb_convert_encoding($str, 'UTF-16BE', $enc);
            return mb_convert_encoding(strrev($str), $enc, 'UTF-16LE');
        }
        

        【讨论】:

          【解决方案5】:

          Grapheme 函数比 mbstring 和 PCRE 函数更正确地处理 UTF-8 字符串/Mbstring 和 PCRE 可能会破坏字符。你可以通过执行以下代码来查看它们之间的差异。

          function str_to_array($string)
          {
              $length = grapheme_strlen($string);
              $ret = [];
          
              for ($i = 0; $i < $length; $i += 1) {
          
                  $ret[] = grapheme_substr($string, $i, 1);
              }
          
              return $ret;
          }
          
          function str_to_array2($string)
          {
              $length = mb_strlen($string, "UTF-8");
              $ret = [];
          
              for ($i = 0; $i < $length; $i += 1) {
          
              $ret[] = mb_substr($string, $i, 1, "UTF-8");
          }
          
              return $ret;
          }
          
          function str_to_array3($string)
          {
              return preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
          }
          
          function utf8_strrev($string)
          {
              return implode(array_reverse(str_to_array($string)));
          }
          
          function utf8_strrev2($string)
          {
              return implode(array_reverse(str_to_array2($string)));
          }
          
          function utf8_strrev3($string)
          {
              return implode(array_reverse(str_to_array3($string)));
          }
          
          // http://www.php.net/manual/en/function.grapheme-strlen.php
          $string = "a\xCC\x8A"  // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
                   ."o\xCC\x88"; // 'LATIN SMALL LETTER O WITH DIAERESIS'  (U+00F6)
          
          var_dump(array_map(function($elem) { return strtoupper(bin2hex($elem)); },
          [
            'should be' => "o\xCC\x88"."a\xCC\x8A",
            'grapheme' => utf8_strrev($string),
            'mbstring' => utf8_strrev2($string),
            'pcre' => utf8_strrev3($string)
          ]));
          

          结果在这里。

          array(4) {
            ["should be"]=>
            string(12) "6FCC8861CC8A"
            ["grapheme"]=>
            string(12) "6FCC8861CC8A"
            ["mbstring"]=>
            string(12) "CC886FCC8A61"
            ["pcre"]=>
            string(12) "CC886FCC8A61"
          }
          

          从 PHP 5.5 (intl 3.0) 开始可以使用 IntlBreakIterator;

          function utf8_strrev($str)
          {
              $it = IntlBreakIterator::createCodePointInstance();
              $it->setText($str);
          
              $ret = '';
              $pos = 0;
              $prev = 0;
          
              foreach ($it as $pos) {
                  $ret = substr($str, $prev, $pos - $prev) . $ret;
                  $prev = $pos;
              }
          
              return $ret;  
          }
          

          【讨论】:

          • 由于几乎所有的字形和mbstring函数都无法处理无效的字节序列,因此需要用替换字符替换无效的字节序列。如果您对该主题感兴趣,请参阅stackoverflow.com/a/13695364/531320 了解详情。
          【解决方案6】:

          这很容易utf8_strrev( $str )。请参阅我在下面复制的我的库的相关 source 代码:

          function utf8_strrev( $str )
          {
              return implode( array_reverse( utf8_split( $str ) ) );
          }
          
          function utf8_split( $str , $split_length = 1 )
          {
              $str    = ( string ) $str;
          
              $ret    = array( );
          
              if( pcre_utf8_support( ) )
              {
                  $str    = utf8_clean( $str );
          
                  $ret    = preg_split('/(?<!^)(?!$)/u', $str );
          
                  // \X is buggy in many recent versions of PHP
                  //preg_match_all( '/\X/u' , $str , $ret );
                  //$ret  = $ret[0];
              }
              else
              {
                  //Fallback
          
                  $len    = strlen( $str );
          
                  for( $i = 0 ; $i < $len ; $i++ )
                  {
                      if( ( $str[$i] & "\x80" ) === "\x00" )
                      {
                          $ret[]  = $str[$i];
                      }
                      else if( ( ( $str[$i] & "\xE0" ) === "\xC0" ) && ( isset( $str[$i+1] ) ) )
                      {
                          if( ( $str[$i+1] & "\xC0" ) === "\x80" )
                          {
                              $ret[]  = $str[$i] . $str[$i+1];
          
                              $i++;
                          }
                      }
                      else if( ( ( $str[$i] & "\xF0" ) === "\xE0" ) && ( isset( $str[$i+2] ) ) )
                      {
                          if( ( ( $str[$i+1] & "\xC0" ) === "\x80" ) && ( ( $str[$i+2] & "\xC0" ) === "\x80" ) )
                          {
                              $ret[]  = $str[$i] . $str[$i+1] . $str[$i+2];
          
                              $i  = $i + 2;
                          }
                      }
                      else if( ( ( $str[$i] & "\xF8" ) === "\xF0" ) && ( isset( $str[$i+3] ) ) )
                      {
                          if( ( ( $str[$i+1] & "\xC0" ) === "\x80" ) && ( ( $str[$i+2] & "\xC0" ) === "\x80" ) && ( ( $str[$i+3] & "\xC0" ) === "\x80" ) )
                          {
                              $ret[]  = $str[$i] . $str[$i+1] . $str[$i+2] . $str[$i+3];
          
                              $i  = $i + 3;
                          }
                      }
                  }
              }
          
          
              if( $split_length > 1 )
              {
                  $ret = array_chunk( $ret , $split_length );
          
                  $ret    = array_map( 'implode' , $ret );
              }
          
              if( $ret[0] === '' )
              {
                  return array( );
              }
          
              return $ret;
          }
          
          
          function utf8_clean( $str , $remove_bom = false )
          {
              $regx = '/([\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})|./s';
          
              $str    = preg_replace( $regx , '$1' , $str );
          
              if( $remove_bom )
              {
                  $str    = utf8_str_replace( utf8_bom( ) , '' , $str );
              }
          
              return $str;
          }
          
          
          function utf8_str_replace( $search , $replace , $subject , &$count = 0 )
          {
              return str_replace( $search , $replace , $subject , $count );
          }
          
          
          function utf8_bom( )
          {
              return "\xef\xbb\xbf";
          
          }
          
          
          function pcre_utf8_support( )
          {
              static $support;
          
              if( !isset( $support ) )
              {
                  $support = @preg_match( '//u', '' );
                  //Cached the response
              }
          
              return $support;
          }
          

          【讨论】:

            猜你喜欢
            • 2020-04-25
            • 2011-11-20
            • 1970-01-01
            • 2011-10-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-02-28
            相关资源
            最近更新 更多