2010-08-15 9 views
6

Quiero asegurarme de que algunos de los reemplazos de cadenas que estoy ejecutando son seguros para varios bytes. He encontrado algunas funciones mb_str_replace en la red, pero son lentas. Estoy hablando de un aumento del 20% después de pasar tal vez 500-900 bytes a través de él.mb_str_replace() ... es lento. alguna alternativa?

¿Alguna recomendación? Estoy pensando en usar preg_replace ya que es nativo y compilado, por lo que podría ser más rápido. Cualquier pensamiento sería apreciado.

+0

Es necesario dar más información. ¿Cuál es la cadena de reemplazo y la codificación del tema? Si el sujeto es UTF-8 y la cadena de reemplazo está en el rango ASCII, puede usar 'str_replace'. – Artefacto

+0

Unicode ha existido desde hace 15 años. ¿Todavía está borrando con cadenas mb en un bucle central interno? Trabaja desde adentro hacia afuera. –

Respuesta

10

Como dijo there, str_replace es seguro de usar en contextos de utf-8, siempre y cuando todos los parámetros sean utf-8 válidos, ya que no habrá ninguna coincidencia ambigua entre ambas cadenas codificadas en varios bytes. Si comprueba la validez de su entrada, entonces no necesita buscar una función diferente.

+3

Esto es incorrecto si está trabajando con Unicode y le interesa la [equivalencia de Unicode] (http://en.wikipedia.org/wiki/Unicode_equivalence). En Unicode, varias secuencias de bytes diferentes pueden representar el mismo personaje. Usar 'str_replace' funcionaría ** solo ** si normalizas tus dos cadenas primero. – Qtax

+0

Un buen consejo, de todos modos mi comprensión de "son seguros para varios discos" es "no darán ningún falso positivo mientras coinciden", lo que en la práctica significa que no dañarán la información de salida en términos de lo que se desea para el reemplazo. –

+0

compruebe el enlace proporcionado – Trix

3

Como la codificación es un verdadero desafío cuando hay entradas de todas partes (utf8 u otras), prefiero usar solo funciones de seguridad de multibyte. Para str_replace, estoy usando this one que es lo suficientemente rápido.

if (!function_exists('mb_str_replace')) 
{ 
    function mb_str_replace($search, $replace, $subject, &$count = 0) 
    { 
     if (!is_array($subject)) 
     { 
     $searches = is_array($search) ? array_values($search) : array($search); 
     $replacements = is_array($replace) ? array_values($replace) : array($replace); 
     $replacements = array_pad($replacements, count($searches), ''); 
     foreach ($searches as $key => $search) 
     { 
      $parts = mb_split(preg_quote($search), $subject); 
      $count += count($parts) - 1; 
      $subject = implode($replacements[$key], $parts); 
     } 
     } 
     else 
     { 
     foreach ($subject as $key => $value) 
     { 
      $subject[$key] = mb_str_replace($search, $replace, $value, $count); 
     } 
     } 
     return $subject; 
    } 
} 
2

Aquí está mi aplicación, con sede fuera Alain's answer:

/** 
* Replace all occurrences of the search string with the replacement string. Multibyte safe. 
* 
* @param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles. 
* @param string|array $replace The replacement value that replaces found search values. An array may be used to designate multiple replacements. 
* @param string|array $subject The string or array being searched and replaced on, otherwise known as the haystack. 
*        If subject is an array, then the search and replace is performed with every entry of subject, and the return value is an array as well. 
* @param string $encoding The encoding parameter is the character encoding. If it is omitted, the internal character encoding value will be used. 
* @param int $count If passed, this will be set to the number of replacements performed. 
* @return array|string 
*/ 
public static function mbReplace($search, $replace, $subject, $encoding = 'auto', &$count=0) { 
    if(!is_array($subject)) { 
     $searches = is_array($search) ? array_values($search) : [$search]; 
     $replacements = is_array($replace) ? array_values($replace) : [$replace]; 
     $replacements = array_pad($replacements, count($searches), ''); 
     foreach($searches as $key => $search) { 
      $replace = $replacements[$key]; 
      $search_len = mb_strlen($search, $encoding); 

      $sb = []; 
      while(($offset = mb_strpos($subject, $search, 0, $encoding)) !== false) { 
       $sb[] = mb_substr($subject, 0, $offset, $encoding); 
       $subject = mb_substr($subject, $offset + $search_len, null, $encoding); 
       ++$count; 
      } 
      $sb[] = $subject; 
      $subject = implode($replace, $sb); 
     } 
    } else { 
     foreach($subject as $key => $value) { 
      $subject[$key] = self::mbReplace($search, $replace, $value, $encoding, $count); 
     } 
    } 
    return $subject; 
} 

Su no acepta una codificación de caracteres, aunque supongo que se podría configurar a través del mb_regex_encoding.

Mis pruebas unitarias pasan:

function testMbReplace() { 
    $this->assertSame('bbb',Str::mbReplace('a','b','aaa','auto',$count1)); 
    $this->assertSame(3,$count1); 
    $this->assertSame('ccc',Str::mbReplace(['a','b'],['b','c'],'aaa','auto',$count2)); 
    $this->assertSame(6,$count2); 
    $this->assertSame("\xbf\x5c\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'iso-8859-1')); 
    $this->assertSame("\xbf\x27",Str::mbReplace("\x27","\x5c\x27","\xbf\x27",'gbk')); 
}