Ich bin auf der Suche nach einer allgemeinen Strategie/einem Ratgeber zum Umgang mit ungültigen UTF-8-Eingaben von Benutzern.
Obwohl meine Webapp-Anwendung UTF-8 verwendet, geben manche Benutzer irgendwie ungültige Zeichen ein. Dies führt zu Fehlern in PHP json_encode () und scheint insgesamt eine schlechte Idee zu sein.
W3C I18N FAQ: Mehrsprachige Formulare sagt "Wenn Nicht-UTF-8-Daten empfangen werden, sollte eine Fehlernachricht zurückgesendet werden.".
EDIT: Ich bin mit der mbstring-Erweiterung sehr vertraut und frage nicht, "wie funktioniert UTF-8 in PHP".) Ich hätte gerne Rat von Leuten, die Erfahrung in realen Situationen haben, wie sie damit umgehen.
EDIT2: Als Teil der Lösung möchte ich wirklich eine fast -Methode sehen, um ungültige Zeichen in U + FFFD umzuwandeln.
Das accept-charset="UTF-8"
-Attribut ist nur eine Richtlinie für Browser, die nicht befolgt werden muss. Auf diese Weise sind sie nicht dazu verpflichtet.
Normalerweise ignoriere ich schlechte Zeichen entweder über iconv()
oder mit den weniger zuverlässigen utf8_encode()
/ - utf8_decode()
Funktionen. Wenn Sie iconv
verwenden, haben Sie auch die Möglichkeit, schlechte Zeichen zu transliterieren.
Hier ist ein Beispiel mit iconv()
:
$str_ignore = iconv('UTF-8', 'UTF-8//IGNORE', $str);
$str_translit = iconv('UTF-8', 'UTF-8//TRANSLIT', $str);
Wenn Sie Ihren Benutzern eine Fehlermeldung anzeigen möchten, würde ich dies wahrscheinlich auf eine globale Art und nicht auf eine pro Wert erhaltene Basis tun. Etwas wie dieses würde wahrscheinlich gut funktionieren:
function utf8_clean($str)
{
return iconv('UTF-8', 'UTF-8//IGNORE', $str);
}
$clean_GET = array_map('utf8_clean', $_GET);
if (serialize($_GET) != serialize($clean_GET))
{
$_GET = $clean_GET;
$error_msg = 'Your data is not valid UTF-8 and has been stripped.';
}
// $_GET is clean!
Möglicherweise möchten Sie auch neue Zeilen normalisieren und (nicht-) sichtbare Steuerzeichen wie folgt entfernen:
function Clean($string, $control = true)
{
$string = iconv('UTF-8', 'UTF-8//IGNORE', $string);
if ($control === true)
{
return preg_replace('~\p{C}+~u', '', $string);
}
return preg_replace(array('~\r\n?~', '~[^\P{C}\t\n]+~u'), array("\n", ''), $string);
}
Code zum Konvertieren von UTF-8 in Unicode-Codepunkte:
function Codepoint($char)
{
$result = null;
$codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));
if (is_array($codepoint) && array_key_exists(1, $codepoint))
{
$result = sprintf('U+%04X', $codepoint[1]);
}
return $result;
}
echo Codepoint('à'); // U+00E0
echo Codepoint('ひ'); // U+3072
Wahrscheinlich schneller als jede andere Alternative, jedoch nicht ausgiebig getestet.
Beispiel:
$string = 'hello world�';
// U+FFFEhello worldU+FFFD
echo preg_replace_callback('/[\p{So}\p{Cf}\p{Co}\p{Cs}\p{Cn}]/u', 'Bad_Codepoint', $string);
function Bad_Codepoint($string)
{
$result = array();
foreach ((array) $string as $char)
{
$codepoint = unpack('N', iconv('UTF-8', 'UCS-4BE', $char));
if (is_array($codepoint) && array_key_exists(1, $codepoint))
{
$result[] = sprintf('U+%04X', $codepoint[1]);
}
}
return implode('', $result);
}
Ist es das, wonach Sie gesucht haben?
Der Empfang ungültiger Zeichen aus Ihrer Web-App hat möglicherweise mit den für HTML-Formulare angenommenen Zeichensätzen zu tun. Sie können angeben, welcher Zeichensatz für Formulare mit dem Attribut accept-charset
verwendet werden soll:
<form action="..." accept-charset="UTF-8">
Sie können auch ähnliche Fragen in StackOverflow mit Zeigern zum Umgang mit ungültigen Zeichen betrachten, z. die in der rechten Spalte, aber ich denke, dass das Signalisieren eines Fehlers für den Benutzer besser ist als der Versuch, die ungültigen Zeichen zu bereinigen, die einen unerwarteten Verlust wichtiger Daten oder eine unerwartete Änderung der Eingaben des Benutzers verursachen.
Ich habe eine ziemlich einfache Klasse zusammengestellt, um zu überprüfen, ob die Eingabe in UTF-8 erfolgt und wie es notwendig ist, utf8_encode()
auszuführen:
class utf8
{
/**
* @param array $data
* @param int $options
* @return array
*/
public static function encode(array $data)
{
foreach ($data as $key=>$val) {
if (is_array($val)) {
$data[$key] = self::encode($val, $options);
} else {
if (false === self::check($val)) {
$data[$key] = utf8_encode($val);
}
}
}
return $data;
}
/**
* Regular expression to test a string is UTF8 encoded
*
* RFC3629
*
* @param string $string The string to be tested
* @return bool
*
* @link http://www.w3.org/International/questions/qa-forms-utf-8.en.php
*/
public static function check($string)
{
return preg_match('%^(?:
[\x09\x0A\x0D\x20-\x7E] # ASCII
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
)*$%xs',
$string);
}
}
// For example
$data = utf8::encode($_POST);
Ich empfehle, nur den Müll nicht zuzulassen. Verlassen Sie sich nicht auf benutzerdefinierte Funktionen, die Ihr System zum Stillstand bringen können. Vergleichen Sie einfach die übermittelten Daten mit einem von Ihnen entworfenen Alphabet. Erstellen Sie eine akzeptable Alphabetzeichenfolge, und bewegen Sie die übermittelten Daten Byte für Byte wie ein Array. Übertragen Sie akzeptable Zeichen in eine neue Zeichenfolge und lassen Sie inakzeptable Zeichen aus. Die Daten, die Sie in Ihrer Datenbank speichern, sind Daten, die vom Benutzer ausgelöst werden, nicht jedoch die vom Benutzer angegebenen Daten.
EDIT # 4: Ersetzen eines schlechten Zeichens durch ein Zeichen: ent
BEARBEITEN # 3: Aktualisiert: 22. September 2010 @ 13:32 Uhr Grund: Die zurückgegebene Zeichenfolge ist UTF-8, und ich habe die Testdatei verwendet, die Sie als Beweis bereitgestellt haben.
<?php
// build alphabet
// optionally you can remove characters from this array
$alpha[]= chr(0); // null
$alpha[]= chr(9); // tab
$alpha[]= chr(10); // new line
$alpha[]= chr(11); // tab
$alpha[]= chr(13); // carriage return
for ($i = 32; $i <= 126; $i++) {
$alpha[]= chr($i);
}
/* remove comment to check ascii ordinals */
// /*
// foreach ($alpha as $key=>$val){
// print ord($val);
// print '<br/>';
// }
// print '<hr/>';
//*/
//
// //test case #1
//
// $str = 'afsjdfhasjhdgljhasdlfy42we875y342q8957y2wkjrgSAHKDJgfcv kzXnxbnSXbcv '.chr(160).chr(127).chr(126);
//
// $string = teststr($alpha,$str);
// print $string;
// print '<hr/>';
//
// //test case #2
//
// $str = ''.'©?™???';
// $string = teststr($alpha,$str);
// print $string;
// print '<hr/>';
//
// $str = '©';
// $string = teststr($alpha,$str);
// print $string;
// print '<hr/>';
$file = 'http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt';
$testfile = implode(chr(10),file($file));
$string = teststr($alpha,$testfile);
print $string;
print '<hr/>';
function teststr(&$alpha, &$str){
$strlen = strlen($str);
$newstr = chr(0); //null
$x = 0;
if($strlen >= 2){
for ($i = 0; $i < $strlen; $i++) {
$x++;
if(in_array($str[$i],$alpha)){
// passed
$newstr .= $str[$i];
}else{
// failed
print 'Found out of scope character. (ASCII: '.ord($str[$i]).')';
print '<br/>';
$newstr .= '�';
}
}
}elseif($strlen <= 0){
// failed to qualify for test
print 'Non-existent.';
}elseif($strlen === 1){
$x++;
if(in_array($str,$alpha)){
// passed
$newstr = $str;
}else{
// failed
print 'Total character failed to qualify.';
$newstr = '�';
}
}else{
print 'Non-existent (scope).';
}
if(mb_detect_encoding($newstr, "UTF-8") == "UTF-8"){
// skip
}else{
$newstr = utf8_encode($newstr);
}
// test encoding:
if(mb_detect_encoding($newstr, "UTF-8")=="UTF-8"){
print 'UTF-8 :D<br/>';
}else{
print 'ENCODED: '.mb_detect_encoding($newstr, "UTF-8").'<br/>';
}
return $newstr.' (scope: '.$x.', '.$strlen.')';
}
Für die Vollständigkeit dieser Frage (nicht unbedingt die beste Antwort) ...
function as_utf8($s) {
return mb_convert_encoding($s, "UTF-8", mb_detect_encoding($s));
}
Es gibt eine Multibyte-Erweiterung für PHP. Schauen Sie sich das an: http://www.php.net/manual/de/book.mbstring.php
Sie sollten die Funktion mb_check_encoding () ausprobieren.
Viel Glück!
Versuchen Sie, das zu tun, was Rails tut, damit alle Browser immer UTF-8-Daten bereitstellen:
<form accept-charset="UTF-8" action="#{action}" method="post"><div
style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
</div>
<!-- form fields -->
</form>
Siehe railssnowman.info oder den ursprünglichen Patch für eine Erklärung.
meta http-equiv
-Tag).accept-charset="UTF-8"
im Formular, auch wenn der Benutzer mit der Seitencodierung herumfummelt (Browser lassen dies zu).✓
, die nur aus dem Unicode-Zeichensatz stammen kann (und in diesem Beispiel nicht aus dem koreanischen Zeichensatz).Wie wäre es, wenn Sie alle Zeichen außerhalb Ihrer angegebenen Untermenge entfernen. Zumindest in einigen Teilen meiner Anwendung würde ich keine Zeichen außerhalb der [a-Z] [0-9-Sets] zulassen, z. B. Benutzernamen. Sie können eine Filterfunktion erstellen, die alle Zeichen außerhalb dieses Bereichs unbemerkt entfernt oder einen Fehler zurückgibt, wenn sie diese erkennt und die Entscheidung an den Benutzer weiterleitet.
Legen Sie UTF-8 als Zeichensatz für alle Header fest, die von Ihrem PHP -Code ausgegeben werden
Geben Sie in jedem PHP - Ausgabeheader UTF-8 als Kodierung an:
header('Content-Type: text/html; charset=utf-8');