douxian6260 2014-10-02 04:38
浏览 57
已采纳

麻烦与php彩虹文本

I have trouble with php rainbow text functions. When I run this function, the output text did not support vietnamese.

For example: "tôi yêu em" ;

<?php   
function rainbow($text)
{

    /*** initialize the return string ***/
    $ret = '';

    /*** an array of colors ***/
    $colors = array(
        'ff00ff', 'ff0099', 'ff0033', 'ff3300',        
        'ff9900', 'ffff00', '99ff00', '33ff00', 
        '00ff33', '00ff99', '00ffff', '0099ff',        
        '0033ff', '3300ff', '9900ff'
        );
    /*** a counter ***/
    $i = 0;    
    /*** get the length of the text ***/
    $textlength = strlen($text);    
    /*** loop over the text ***/
    while($i<=$textlength)
    {
        /*** loop through the colors ***/
        foreach($colors as $value)
        {
            if ($text[$i] != "")
            {
                $ret .= '<span style="color:#'.$value.';">'.$text[$i]."</span>";
            }
        $i++;
        }
    }
    /*** return the highlighted string ***/
    $ret = html_entity_decode($ret, ENT_QUOTES, 'UTF-8');
    return $ret;
}
echo rainbow('tôi yêu em');
?>
  • 写回答

1条回答 默认 最新

  • duanbi3385 2014-10-04 05:31
    关注

    You're going to get uninitialized string offset notices in any language with that function due to the way you're iterating over the string bytes + colors. Better to access the colors via an InfiniteIterator which will just loop around and around.

    Your specific problem with Vietnamese is that some of those characters are composed of multiple bytes. Functions like strlen() and accessing offsets via array brackets like $text[$i] are not multi-byte safe - they work on individual bytes rather than characters.

    While it might be tempting to just use mb_strlen() in place of strlen() to get the number of characters rather than the number of bytes, and mb_substr() rather than $text[$i] to get a character rather than a byte, you'll still end up breaking up graphemes like (which is here encoded as e followed by a combining grave accent.) A solution is to break up the string into an array with a regular expression.


    Example:

    function rainbow($text)
    {
        $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
        $return = '';
    
        $colors = new InfiniteIterator(
            new ArrayIterator(
                ['ff00ff', 'ff0099', 'ff0033', 'ff3300',
                 'ff9900', 'ffff00', '99ff00', '33ff00',
                 '00ff33', '00ff99', '00ffff', '0099ff',
                 '0033ff', '3300ff', '9900ff']
            )
        );
        $colors->rewind();
    
        // Match any codepoint along with any combining marks.
        preg_match_all('/.\pM*+/su', $text, $matches);
        foreach ($matches[0] as $char)
        {
            if (preg_match('/^\pZ$/u', $char)) {
                // No need to color whitespace or invisible separators.
                $return .= $char;
            } else {
                $return .= "<span style='color:#{$colors->current()};'>$char</span>";
                $colors->next();
            }
        }
    
        return $return;
    }
    
    echo rainbow('tôi yêu em eve&#x300; foo baz');
    

    Output:

    <span style='color:#ff00ff;'>t</span><span style='color:#ff0099;'>ô</span><span style='color:#ff0033;'>i</span> <span style='color:#ff3300;'>y</span><span style='color:#ff9900;'>ê</span><span style='color:#ffff00;'>u</span> <span style='color:#99ff00;'>e</span><span style='color:#33ff00;'>m</span> <span style='color:#00ff33;'>e</span><span style='color:#00ff99;'>v</span><span style='color:#00ffff;'>è</span> <span style='color:#0099ff;'>f</span><span style='color:#0033ff;'>o</span><span style='color:#3300ff;'>o</span> <span style='color:#9900ff;'>b</span><span style='color:#ff00ff;'>a</span><span style='color:#ff0099;'>z</span>

    </div>
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?