135 votes

Suppression des caractères non-utf8 d'une chaîne de caractères

J'ai un problème avec la suppression des caractères non-utf8 de la chaîne, qui ne s'affichent pas correctement. Les caractères sont les suivants 0x97 0x61 0x6C 0x6F (représentation hexagonale)

Quelle est la meilleure façon de les enlever ? Expression régulière ou autre chose ?

149voto

Si vous appliquez utf8_encode() à une chaîne déjà UTF8, vous obtiendrez une sortie UTF8 déformée.

J'ai créé une fonction qui résout tous ces problèmes. Elle s'appelle Encoding::toUTF8().

Vous n'avez pas besoin de savoir quel est l'encodage de vos chaînes de caractères. Il peut s'agir de Latin1 (iso 8859-1), de Windows-1252 ou d'UTF8, ou encore d'un mélange des trois. Encoding::toUTF8() convertira tout en UTF8.

Je l'ai fait parce qu'un service me donnait un flux de données tout chamboulé, mélangeant ces encodages dans la même chaîne.

Utilisation :

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::toUTF8($mixed_string);

$latin1_string = Encoding::toLatin1($mixed_string);

J'ai inclus une autre fonction, Encoding::fixUTF8(), qui corrigera toutes les chaînes UTF8 qui ont l'air déformées parce qu'elles ont été encodées en UTF8 plusieurs fois.

Utilisation :

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

Exemples :

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");

produira un résultat :

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football

Télécharger :

https://github.com/neitanod/forceutf8

101voto

Markus Jarderot Points 33893

En utilisant une approche 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);

Il recherche les séquences UTF-8, et les capture dans le groupe 1. Il correspond également aux octets uniques qui n'ont pas pu être identifiés comme faisant partie d'une séquence UTF-8, mais ne les capture pas. Le remplacement est ce qui a été capturé dans le groupe 1. Ceci élimine effectivement tous les octets invalides.

Il est possible de réparer la chaîne en codant les octets invalides en caractères UTF-8. Mais si les erreurs sont aléatoires, cela pourrait laisser des symboles étranges.

$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);

EDIT :

  • !empty(x) correspondra à des valeurs non vides ( "0" est considéré comme vide).
  • x != "" correspondra à des valeurs non vides, y compris "0" .
  • x !== "" correspondra à tout sauf "" .

x != "" semble la meilleure à utiliser dans ce cas.

J'ai également accéléré un peu la correspondance. Au lieu de faire correspondre chaque caractère séparément, elle fait correspondre des séquences de caractères UTF-8 valides.

91voto

Frosty Z Points 8935

Vous pouvez utiliser mbstring :

$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');

...supprimera les caractères non valides.

Voir : Remplacement des caractères UTF-8 invalides par des points d'interrogation, mbstring.substitute_character semble ignoré

39voto

mwangi Points 768

Le dessous a bien fonctionné.

<?php

$string = "Remove these characters: äó";
$string = preg_replace('/[^(\x20-\x7F)]*/','', $string);

?>

Voir la source ici

22voto

Znarkus Points 5025
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);

C'est ce que j'utilise. Il semble fonctionner assez bien. Tiré de http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X