php - regular - Eliminar caracteres no utf8 de la cadena
regex to preg_match (15)
¿Qué hay de iconv:
http://php.net/manual/en/function.iconv.php
No lo he usado dentro de PHP, pero siempre me ha funcionado bien en la línea de comando. Puede hacer que sustituya los caracteres no válidos.
Tengo un problema con la eliminación de caracteres no utf8 de la cadena, que no se muestran correctamente. Los caracteres son así 0x97 0x61 0x6C 0x6F (representación hexadecimal)
¿Cuál es la mejor manera de eliminarlos? Expresión regular o algo más?
Así que las reglas son que el primer Octlet UTF-8 tiene el bit alto configurado como marcador, y luego de 1 a 4 bits para indicar cuántos octlets adicionales; entonces cada uno de los octlets adicionales debe tener los dos bits altos establecidos en 10.
El pseudo-pitón sería:
newstring = ''''
cont = 0
for each ch in string:
if cont:
if (ch >> 6) != 2: # high 2 bits are 10
# do whatever, e.g. skip it, or skip whole point, or?
else:
# acceptable continuation of multi-octlet char
newstring += ch
cont -= 1
else:
if (ch >> 7): # high bit set?
c = (ch << 1) # strip the high bit marker
while (c & 1): # while the high bit indicates another octlet
c <<= 1
cont += 1
if cont > 4:
# more than 4 octels not allowed; cope with error
if !cont:
# illegal, do something sensible
newstring += ch # or whatever
if cont:
# last utf-8 was not terminated, cope
Esta misma lógica debe ser traducible a php. Sin embargo, no está claro qué tipo de stripping se hará una vez que obtenga un personaje mal formado.
Desde el parche reciente al módulo analizador JSON de Drupal''s Feeds:
//remove everything except valid letters (from any language)
$raw = preg_replace(''/(?:////u[/pL/p{Zs}])+/'', '''', $raw);
Si te preocupa sí, conserva espacios como caracteres válidos.
Hice lo que necesitaba. Elimina los extensos caracteres emoji actuales que no encajan en el conjunto de caracteres ''utf8'' de MySQL y que me dieron errores como "SQLSTATE [HY000]: Error general: 1366 Valor de cadena incorrecto".
Para detalles, ver https://www.drupal.org/node/1824506#comment-6881382
El texto puede contener caracteres no utf8 . Intenta hacer primero:
$nonutf8 = mb_convert_encoding($nonutf8 , ''UTF-8'', ''UTF-8'');
Puedes leer más sobre esto aquí: http://php.net/manual/en/function.mb-convert-encoding.php news
He creado una función que elimina caracteres UTF-8 no válidos de una cadena. Lo estoy usando para borrar la descripción de 27000 productos antes de que genere el archivo de exportación XML.
public function stripInvalidXml($value) {
$ret = "";
$current;
if (empty($value)) {
return $ret;
}
$length = strlen($value);
for ($i=0; $i < $length; $i++) {
$current = ord($value{$i});
if (($current == 0x9) || ($current == 0xA) || ($current == 0xD) || (($current >= 0x20) && ($current <= 0xD7FF)) || (($current >= 0xE000) && ($current <= 0xFFFD)) || (($current >= 0x10000) && ($current <= 0x10FFFF))) {
$ret .= chr($current);
}
else {
$ret .= "";
}
}
return $ret;
}
Ligeramente diferente a la pregunta, pero lo que estoy haciendo es usar HtmlEncode (cadena),
pseudo código aquí
var encoded = HtmlEncode(string);
encoded = Regex.Replace(encoded, "&#/d+?;", "");
var result = HtmlDecode(encoded);
entrada y salida
"Headlight/x007E Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
"Headlight~ Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
Sé que no es perfecto, pero hace el trabajo por mí.
Para eliminar todos los caracteres Unicode fuera del plano de lenguaje básico Unicode:
$str = preg_replace("/[^//x00-//xFFFF]/", "", $str);
Puedes usar mbstring:
$text = mb_convert_encoding($text, ''UTF-8'', ''UTF-8'');
... eliminará los caracteres no válidos.
Si aplica utf8_encode()
a una cadena UTF8, devolverá una salida UTF8 ilegible.
Hice una función que aborda todos estos problemas. Se llama Encoding::toUTF8()
.
No necesita saber cuál es la codificación de sus cadenas. Puede ser Latin1 (ISO8859-1), Windows-1252 o UTF8, o la cadena puede tener una combinación de ellos. Encoding::toUTF8()
convertirá todo a UTF8.
Lo hice porque un servicio me estaba dando una fuente de datos mezclada, mezclando esas codificaciones en la misma cadena.
Uso:
require_once(''Encoding.php'');
use /ForceUTF8/Encoding; // It''s namespaced now.
$utf8_string = Encoding::toUTF8($mixed_string);
$latin1_string = Encoding::toLatin1($mixed_string);
He incluido otra función, Encoding :: fixUTF8 (), que reparará todas las cadenas UTF8 que parecen ilegibles producto de haber sido codificadas en UTF8 varias veces.
Uso:
require_once(''Encoding.php'');
use /ForceUTF8/Encoding; // It''s namespaced now.
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
Ejemplos:
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
dará salida:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Descargar:
UConverter se puede usar desde PHP 5.5. UConverter es mejor elección si usas la extensión intl y no usas mbstring.
function replace_invalid_byte_sequence($str)
{
return UConverter::transcode($str, ''UTF-8'', ''UTF-8'');
}
function replace_invalid_byte_sequence2($str)
{
return (new UConverter(''UTF-8'', ''UTF-8''))->convert($str);
}
htmlspecialchars se puede utilizar para eliminar la secuencia de bytes no válida desde PHP 5.4. Htmlspecialchars es mejor que preg_match para manejar gran tamaño de byte y la precisión. Se puede ver mucha implementación incorrecta al usar expresión regular.
function replace_invalid_byte_sequence3($str)
{
return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, ''UTF-8''));
}
Usando un enfoque regex:
$regex = <<<''END''
/
(
(?: [/x00-/x7F] # single-byte sequences 0xxxxxxx
| [/xC0-/xDF][/x80-/xBF] # double-byte sequences 110xxxxx 10xxxxxx
| [/xE0-/xEF][/x80-/xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [/xF0-/xF7][/x80-/xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3
){1,100} # ...one or more times
)
| . # anything else
/x
END;
preg_replace($regex, ''$1'', $text);
Busca secuencias UTF-8 y las captura en el grupo 1. También coincide con bytes individuales que no se pudieron identificar como parte de una secuencia UTF-8, pero no los captura. El reemplazo es lo que fue capturado en el grupo 1. Esto elimina efectivamente todos los bytes inválidos.
Es posible reparar la cadena codificando los bytes inválidos como caracteres UTF-8. Pero si los errores son aleatorios, esto podría dejar algunos símbolos extraños.
$regex = <<<''END''
/
(
(?: [/x00-/x7F] # single-byte sequences 0xxxxxxx
| [/xC0-/xDF][/x80-/xBF] # double-byte sequences 110xxxxx 10xxxxxx
| [/xE0-/xEF][/x80-/xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [/xF0-/xF7][/x80-/xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3
){1,100} # ...one or more times
)
| ( [/x80-/xBF] ) # invalid byte in range 10000000 - 10111111
| ( [/xC0-/xFF] ) # invalid byte in range 11000000 - 11111111
/x
END;
function utf8replacer($captures) {
if ($captures[1] != "") {
// Valid byte sequence. Return unmodified.
return $captures[1];
}
elseif ($captures[2] != "") {
// Invalid byte of the form 10xxxxxx.
// Encode as 11000010 10xxxxxx.
return "/xC2".$captures[2];
}
else {
// Invalid byte of the form 11xxxxxx.
// Encode as 11000011 10xxxxxx.
return "/xC3".chr(ord($captures[3])-64);
}
}
preg_replace_callback($regex, "utf8replacer", $text);
EDITAR:
-
!empty(x)
coincidirá con los valores no vacíos ("0"
se considera vacío). -
x != ""
coincidirá con valores no vacíos, incluido"0"
. -
x !== ""
coincidirá con cualquier cosa excepto""
.
x != ""
parece ser el mejor para usar en este caso.
También aceleré el partido un poco. En lugar de emparejar cada carácter por separado, coincide con secuencias de caracteres UTF-8 válidos.
prueba esto:
$string = iconv("UTF-8","UTF-8//IGNORE",$string);
De acuerdo con el manual de iconv , la función tomará el primer parámetro como el juego de caracteres de entrada, el segundo parámetro como el conjunto de caracteres de salida y el tercero como la cadena de entrada real.
Si configura el juego de caracteres de entrada y salida en UTF-8 y agrega el //IGNORE
al juego de caracteres de salida, la función colocará (eliminará) todos los caracteres en la cadena de entrada que no pueden ser representados por el juego de caracteres de salida. Por lo tanto, filtrando la cadena de entrada en efecto.
Esta función elimina todos los caracteres NO ASCII, es útil pero no resuelve la pregunta:
Esta es mi función que siempre funciona, independientemente de la codificación:
function remove_bs($Str) {
$StrArr = str_split($Str); $NewStr = '''';
foreach ($StrArr as $Char) {
$CharNo = ord($Char);
if ($CharNo == 163) { $NewStr .= $Char; continue; } // keep £
if ($CharNo > 31 && $CharNo < 127) {
$NewStr .= $Char;
}
}
return $NewStr;
}
Cómo funciona:
echo remove_bs(''Hello õhowå åare youÆ?''); // Hello how are you?
$string = preg_replace(''~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i'', ''$1'', htmlentities($string, ENT_COMPAT, ''UTF-8''));
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);
Esto es lo que estoy usando. Parece funcionar bastante bien. Tomado de http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/