¿Cómo sustituir caracteres no SGML en String usando PHP?

Programé un libro de visitas usando PHP4 y HTML 4.01 (con el juego de caracteres ISO-8859-15, es decir, latin-9). Los datos se guardan en una base de datos MySQL con el juego de caracteres (ISO-8859-1, es decir, latin-1).

Cuando alguien ingresa caracteres de un conjunto de caracteres diferente, parece que los navegadores envían los datos codificados (en realidad, no he verificado dónde se codifica, …).

De todos modos, en algunos casos, parece que los caracteres no se guardan codificados en la base de datos. Por lo tanto, el validador devuelve un mensaje de error cuando agregue mostrar los datos dentro de un documento HTML4.01:

el número de carácter no SGML 146

Has usado un carácter ilegal en tu texto. HTML utiliza el repertorio de caracteres UNICODE Consortium estándar, y deja indefinidos (entre otros) códigos de 65 caracteres (0 a 31 inclusive y 127 a 159 inclusive) que a veces se usan para comillas tipográficas y similares en juegos de caracteres propios. El validador ha encontrado uno de estos caracteres indefinidos en su documento. El personaje puede aparecer en su navegador como una cita rizada, o un símbolo de marca, o algún otro glifo de fantasía; en una computadora diferente, sin embargo, probablemente aparecerá como un personaje completamente diferente, o nada en absoluto.

Su mejor opción es reemplazar el personaje con el carácter ASCII equivalente más cercano o usar una entidad de carácter apropiada. Para obtener más información sobre la encoding de caracteres en la web, consulte la excelente referencia de Problemas de conjunto de caracteres HTML de Alan Flavell.

Este error también se puede desencadenar formateando caracteres incrustados en documentos por algunos procesadores de texto. Si usa un procesador de texto para editar sus documentos HTML, asegúrese de usar el comando “Guardar como ASCII” o similar para guardar el documento sin formatear la información.

Ahora estoy usando PHP5.2.17, y jugué un poco con htmlspecialchars, pero nada funcionó. ¿Cómo puedo codificar esos caracteres para que no haya más errores de validación?

Tanto en ISO-8859-1 como en ISO-8859-15, el número de carácter 146 es un carácter de control MW (Mensaje en espera) del rango C1 .

SGML se refiere a ISO 8859-1 (tenga en cuenta el espacio entre ISO y 8859-1, que no es un guión como en los juegos de caracteres que usa). No permite caracteres de control sino tres (aquí: SGML en HTML ):

En el conjunto de caracteres del documento HTML, solo se permiten tres caracteres de control: Pestaña horizontal, Retorno de carro y Avance de línea (posiciones de código 9, 13 y 10).

Por lo tanto, pasaste un personaje ilegal. No existe una entidad SGML / HTML para la que pueda reemplazarlo.

Sugiero que valide la entrada que entra en su aplicación que no permite caracteres de control. Si crees que esos personajes originalmente representaban algo útil, como una carta que realmente se puede leer (por ejemplo, no un personaje de control), es probable que cuando proceses los datos, la encoding se rompa en algún momento.

Según la información que figura en su pregunta, es difícil decir dónde, porque solo especifica la encoding de entrada y la encoding de la base de datos archivada, pero esas dos ya no coinciden (lo que no debería producir el problema que usted pregunta, pero puede producir otros problemas). Junto a esos dos lugares, también está el juego de caracteres de conexión de cliente de base de datos (no especificado en su pregunta), la encoding de salida (no especificada en su pregunta) y la encoding de contenido de respuesta (no especificada en su pregunta).

Puede tener sentido que cambie su encoding general a UTF-8 para admitir una gama más amplia de caracteres, pero eso es realmente una posibilidad .

Editar: La parte de arriba es de alguna manera una vista estricta. Me vino a la mente que la entrada que recibes no es realmente ISO-8859-1 (5) sino algo más, como una página de códigos de Windows. Probablemente diría que es Windows-1252 (cp1252) Wikipedia . En comparación con el rango C1 de ISO-8859-1 (128-159), tiene varios caracteres que no son de control.

La página de Wikipedia también señala que la mayoría de los navegadores tratan a ISO-8859-1 como Windows-1252 / CP1252 / CP-1252. La función PHP htmlentities() no puede tratar con estos caracteres, la tabla de traducción para entidades HTML no cubre los puntos de código (PHP 5.3, no probado contra 5.4). Necesita crear su propia tabla de traducción y usarla con strtr para reemplazar los caracteres no disponibles en ISO 8859-15 para Windows-1252:

 /* * mappings of Windows-1252 (cp1252) 128 (0x80) - 159 (0x9F) characters: * @link http://en.wikipedia.org/wiki/Windows-1252 * @link http://www.w3.org/TR/html4/sgml/entities.html */ $cp1252HTML401Entities = array( "\x80" => '€', # 128 -> euro sign, U+20AC NEW "\x82" => '‚', # 130 -> single low-9 quotation mark, U+201A NEW "\x83" => 'ƒ', # 131 -> latin small f with hook = function = florin, U+0192 ISOtech "\x84" => '„', # 132 -> double low-9 quotation mark, U+201E NEW "\x85" => '…', # 133 -> horizontal ellipsis = three dot leader, U+2026 ISOpub "\x86" => '†', # 134 -> dagger, U+2020 ISOpub "\x87" => '‡', # 135 -> double dagger, U+2021 ISOpub "\x88" => 'ˆ', # 136 -> modifier letter circumflex accent, U+02C6 ISOpub "\x89" => '‰', # 137 -> per mille sign, U+2030 ISOtech "\x8A" => 'Š', # 138 -> latin capital letter S with caron, U+0160 ISOlat2 "\x8B" => '‹', # 139 -> single left-pointing angle quotation mark, U+2039 ISO proposed "\x8C" => 'Œ', # 140 -> latin capital ligature OE, U+0152 ISOlat2 "\x8E" => 'Ž', # 142 -> U+017D "\x91" => '‘', # 145 -> left single quotation mark, U+2018 ISOnum "\x92" => '’', # 146 -> right single quotation mark, U+2019 ISOnum "\x93" => '“', # 147 -> left double quotation mark, U+201C ISOnum "\x94" => '”', # 148 -> right double quotation mark, U+201D ISOnum "\x95" => '•', # 149 -> bullet = black small circle, U+2022 ISOpub "\x96" => '–', # 150 -> en dash, U+2013 ISOpub "\x97" => '—', # 151 -> em dash, U+2014 ISOpub "\x98" => '˜', # 152 -> small tilde, U+02DC ISOdia "\x99" => '™', # 153 -> trade mark sign, U+2122 ISOnum "\x9A" => 'š', # 154 -> latin small letter s with caron, U+0161 ISOlat2 "\x9B" => '›', # 155 -> single right-pointing angle quotation mark, U+203A ISO proposed "\x9C" => 'œ', # 156 -> latin small ligature oe, U+0153 ISOlat2 "\x9E" => 'ž', # 158 -> U+017E "\x9F" => 'Ÿ', # 159 -> latin capital letter Y with diaeresis, U+0178 ISOlat2 ); $outputWithEntities = strtr($output, $cp1252HTML401Entities); 

Si quieres estar aún más seguro, puedes ahorrar las entidades nombradas y solo elegir las numéricas que también deberían funcionar en navegadores muy antiguos:

 $cp1252HTMLNumericEntities = array( "\x80" => '€', # 128 -> euro sign, U+20AC NEW "\x82" => '‚', # 130 -> single low-9 quotation mark, U+201A NEW "\x83" => 'ƒ', # 131 -> latin small f with hook = function = florin, U+0192 ISOtech "\x84" => '„', # 132 -> double low-9 quotation mark, U+201E NEW "\x85" => '…', # 133 -> horizontal ellipsis = three dot leader, U+2026 ISOpub "\x86" => '†', # 134 -> dagger, U+2020 ISOpub "\x87" => '‡', # 135 -> double dagger, U+2021 ISOpub "\x88" => 'ˆ', # 136 -> modifier letter circumflex accent, U+02C6 ISOpub "\x89" => '‰', # 137 -> per mille sign, U+2030 ISOtech "\x8A" => 'Š', # 138 -> latin capital letter S with caron, U+0160 ISOlat2 "\x8B" => '‹', # 139 -> single left-pointing angle quotation mark, U+2039 ISO proposed "\x8C" => 'Œ', # 140 -> latin capital ligature OE, U+0152 ISOlat2 "\x8E" => 'Ž', # 142 -> U+017D "\x91" => '‘', # 145 -> left single quotation mark, U+2018 ISOnum "\x92" => '’', # 146 -> right single quotation mark, U+2019 ISOnum "\x93" => '“', # 147 -> left double quotation mark, U+201C ISOnum "\x94" => '”', # 148 -> right double quotation mark, U+201D ISOnum "\x95" => '•', # 149 -> bullet = black small circle, U+2022 ISOpub "\x96" => '–', # 150 -> en dash, U+2013 ISOpub "\x97" => '—', # 151 -> em dash, U+2014 ISOpub "\x98" => '˜', # 152 -> small tilde, U+02DC ISOdia "\x99" => '™', # 153 -> trade mark sign, U+2122 ISOnum "\x9A" => 'š', # 154 -> latin small letter s with caron, U+0161 ISOlat2 "\x9B" => '›', # 155 -> single right-pointing angle quotation mark, U+203A ISO proposed "\x9C" => 'œ', # 156 -> latin small ligature oe, U+0153 ISOlat2 "\x9E" => 'ž', # 158 -> U+017E "\x9F" => 'Ÿ', # 159 -> latin capital letter Y with diaeresis, U+0178 ISOlat2 ); 

Espero que esto sea más útil ahora. Consulte también la página de Wikipedia vinculada anteriormente para algunos personajes que están en Windows-1242 e ISO 8859-15 pero en diferentes puntos. Probablemente deberías considerar usar UTF-8 en tu sitio web.

Una página web que tiene un campo de entrada de texto debe estar codificada en UTF-8, porque esta es la única forma de garantizar que todos los caracteres ingresados ​​por el usuario se transmitan correctamente. Cómo lidiar con ellos desde el lado del servidor (por ejemplo, rechazar caracteres fuera de un rango específico) es un problema diferente.

Si usa alguna otra encoding y el usuario ingresa un carácter que no tiene representación en esa encoding, esta es una condición de error que los navegadores pueden manejar de la manera que deseen. Los navegadores modernos hacen algo que es muy extraño en principio, aunque útil en la práctica: representan a los personajes como referencias de personajes, como para la comilla simple correcta (‘). En este caso, los datos recibidos son los mismos que si el usuario hubiera escrito los caracteres (pero esto es tan teórico que los proveedores de navegadores aparentemente ignoran el problema).

Lo que sucede en el lado del servidor en su caso no está claro, pero puede implicar muchos tipos de procesamiento. En cualquier caso, no puede almacenar en general ISO-8859-15 en la encoding ISO-8859-1 (ISO-8859-15 fue diseñado para reemplazar algunos caracteres en ISO-8859-1 por otros caracteres). No está claro qué hace su software con las referencias de caracteres como . Sería algo extraño, aunque seguramente posible, que el software los reemplace por referencias de caracteres como (que se basan en el uso de Windows-1252 como conjunto de caracteres del documento, contrariamente a las reglas HTML, técnicamente no están definidos, no son ilegales, en HTML, pero son tan ampliamente admitidos por los navegadores que HTML5 lo convierte en una regla).