[Pregunta]: Detectar acentos en consulta

Iniciado por Leguim, 25 Agosto 2019, 18:22 PM

0 Miembros y 1 Visitante están viendo este tema.

Leguim

Buenos días!

Este es un tema que ya lo publique creo ya hace 2 meses aproximadamente no me acuerdo bien, en fin lo que me pasaba era que digamos tenía un buscador donde naturalmente se esperaba recibir una cadena de texto la cual serviría para buscar coincidencias dentro de una consulta (LIKE %$busqueda%). Después de pasar toda pero toda mi base de datos a 'utf8_spanish_ci' y hacer una re-configuración y mejoras al 100%, en relación a este problema nada cambió sigue sin detectar los acentos.

Mi documento html tiene utf8, mi base de datos esta en utf8_spanish_ci, y no se que otra cosa tengo que cambiar la verdad, vi en otros temas que se debe poner set_charset pero también se dice que eso es una mala práctica.

Código (php) [Seleccionar]

WHERE CONCAT(name, ' ', surname) LIKE '%".($search)."%'

EdePC

Saludos,

- Que raro, he estado probando y esto me funciona perfecto con acentos, por ejemplo pongo: ciá y me muestra a Salomé García

Código (php) [Seleccionar]
<form action="#" method="post">
  <input type="text" name="busqueda" autocomplete="off">
  <input type="submit" value="Buscar">
</form>

<?php

  $busqueda 
= isset($_POST['busqueda']) ? $_POST['busqueda'] : '';

  
$con = new PDO("mysql:host=127.0.0.1;dbname=pers""root""root");
  
$sql "SELECT name, surname FROM persons WHERE CONCAT(name, ' ', surname) LIKE '%$busqueda%'";
  foreach ( 
$con->query($sql) as $row ) {
    echo 
$row['name'] . ' ' $row['surname'] . '<br>';
  }

?>


Código (sql) [Seleccionar]
CREATE DATABASE pers;
USE pers;

CREATE TABLE persons (
  id_person INTEGER PRIMARY KEY AUTO_INCREMENT,
  name      VARCHAR(20),
  surname   VARCHAR(20)
);

INSERT INTO persons VALUES
  ( NULL, 'Álvaro', 'Díaz'  ),
  ( NULL, 'Inés'  , 'López' ),
  ( NULL, 'René'  , 'Guzmán');


- No he echo nada raro, ni configurado nada en MySQL (MariaDB), ni puesto utf-8 en nungún lugar, todo automático.

- Si le hago un Dump a mi base de datos obtengo:

Código (sql) [Seleccionar]
-- MySQL dump 10.17  Distrib 10.3.17-MariaDB, for Win32 (AMD64)
--
-- Host: localhost    Database: pers
-- ------------------------------------------------------
-- Server version 10.3.17-MariaDB

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `persons`
--

DROP TABLE IF EXISTS `persons`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `persons` (
  `id_person` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `surname` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id_person`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;


- Veo que usa InnoDB y utf8 por defecto. Supongo que por defecto todo debería de andar bien. Aquí he encontrado algo más de documentación que quizá debas revisar:

https://www.baulphp.com/pdo-mysql-utf-8-caracteres-especiales-en-php/

MinusFour

#2
Te diría que hicieras un dump de la tabla donde tienes los datos problema pero no estoy muy seguro de que la información que acabe aquí sea exactamente la que tienes en tu base de datos. Sobre todo con el típico copia y pega. Porque tu copias un string como: "Conexión" en el foro directamente de un documento en WP1252 o ISO-8859 que abriste de sublime o notepad++ por ejemplo, nosotros vamos a poder ver "Conexión" pero no el encoding que usaste. Pero si subes el archivo del dump en algún lado (que no haga nada raro con el charset) entonces podemos ver el encoding en el que está.

Los dumps de hoy en día por lo general los recogen los editores correctamente, muy probablemente vengan con BOMs o similar. Entonces si tu abres el archivo y no ves nada raro, es muy probable que el encoding este correcto. Si empiezas a ver cosas como: "Conexi?n" en tu dump entonces hay problemas. Si todo se ve bien, como quiera puede haber problemas si tu editor está leyendo el archivo en otro encoding que no sea utf8. No es extraño que pudiera pasar esto porque UTF-8 y otros encodings pueden confundirse porque la mitad de los encodings que se utilizan son los mismos. Técnicamente, un archivo que solo usa caracteres ASCII puede ser leido perfectamente tanto en UTF-8 como en WP1252 o ISO-8859. Son indistinguibles. Pero cuando empiezas a usar cosas que no son ASCII ahí es cuando el editor de texto puede hacer conjeturas acerca del encoding. Si quieres estar totalmente seguro que la información de tu DUMP este correctamente en UTF-8 tienes que decirle al editor que abra el archivo con ese encoding.

Y todo esto es para poder cerciorarse que lo que tienes en tu base de datos es UTF-8.  

Si crees que necesitas utilizar set_charset, revisa el charset de tu conexión:

Código (php) [Seleccionar]

echo mysqli_character_set_name($mysqli); //$mysqli es tu conexión SQL


Si imprime utf8 todo bien, si imprime algo como latin1 o similar entonces quizás si necesitas usar mysqli_set_charset. También puedes configurar tu servidor para que utilice un charset por defecto en las conexiones:

Código (ini) [Seleccionar]

;porfavor no copiar el [mysqld], buscarlo en el archivo de configuración
;esta de referencia
[mysqld]
character_set_server=utf8
collation-server=utf8_spanish_ci


El character_set_server no es necesario si usas MySQL arriba de 8.0 porque ese es el valor por defecto (técnicamente, utf8mb4)

Leguim

#3
Hola! ya cambié algunas cosas, revise las cosas que me dijeron primero revisar el editor si guarda en el encoding utf8 que estaba bien, luego editar mysql la collation en ese archivo que estaba en utf8_uniqode_ci y lo cambie a utf8_spanish_ci,  pero sigue igual lo que no se ahora es hacer un dump...

No se si sea relevante pero digamos un nombre el mio por ejemplo si le pones la i con acento se vería así en la base de datos:

Míguel y no Míguel, me imagino que esto esta bien porque me codifica a utf8 pero quería señalarlo.

Gracias a los dos!

MinusFour

Entonces si tienes un problema con la información en la base de datos. Yo diría que la información que estás enviando si es UTF-8 pero está siendo codificada en otro encoding en otro lado, posiblemente en la conexión entre tu servidor SQL y cliente SQL por lo que me dices. El hexadecimal de la cadena "Míguel" en UTF-8 es:


0x4d 0xc3 0xad 0x67 0x75 0x65 0x6c


Para 5 de los 7 bytes, la traducción es bastante simple, los puedes buscar en una tabla ASCII.



Los únicos valores que no te vas a encontrar en la tabla son: 0xc3 y 0xad porque no son ASCII. Estos 2 bytes, representan un solo carácter en UTF-8, la i latina pequeña con acento agudo ( í ). ¿Pero que pasa cuando se usa otro encoding? Vamos a ver con WP 1252 (uno de los más comunes):



Resulta ser que en WP-1252 los caracteres cada uno son de 1 byte. Por lo que tenemos 2 caracteres. Uno de ellos es: à y el otro es el carácter invisible SHY. Lo que significa que en lugar de tener una í , tenemos Ã<SHY>. SHY no se imprime por lo que acabamos con Míguel en lugar de Miguel.

PD. Me he equivocado con el nombre del encoding, en mi mente lo tengo como Windows Pages 1252 pero realmente es Code Page 1252 o Windows 1252.

Leguim

#5
No creo que te haya entendido muy bien jeje  ;D pero en base a lo que te pude entender hice esto.

Código (php) [Seleccionar]

       function Buscar_Usuarios_Ajax($search)
{
$con = Conexion("root", "");
echo $search; // imprimo la busqueda (texto)
$search = utf8_encode($search);
echo $search; // imprimo la busqueda (texto) pero codificada a utf8
$consulta = $con->prepare("SELECT id_user, name, surname, avatar FROM usuarios WHERE CONCAT(name, ' ', surname) LIKE '%".($search)."%' ORDER BY CONCAT(name, ' ', surname) ASC LIMIT 6");
$consulta->execute();
$resultados = $consulta->fetchAll();
echo $resultados[0]['name']; // imprimo el nombre encontrado

return $resultados;
}


me devuelve si escribo "Míguel"
echo = "Míguel"
echo = "MÃguel"
naturalmente no va encontrar resultados pero digamos si escribo "M" antes de poner una i sin acento ya que en la base de datos existe "Míguel" pero se almacena "MÃguel"

echo "M"
echo "M"
echo "Míguel" // sería el resultado obtenido o que se muestra

de igual forma sigue sin funcionar

y en breve me parece que va a pasar esto:

EdePC

- Para ser más "crudos" puedes usar un Editor Hexadecimal, por ejemplo el gratuito HXD: https://mh-nexus.de/downloads/HxDSetup.zip

- Luego te diriges a tu carpeta \mysql\data\pers\ donde pers coincide con el nombre de mi base de datos.

- Abres (o arrastras a HxD) el fichero db.opt con HxD, ahí debe de decir el CharSet que usa tu base de datos, en mi caso tiene:

CitarOffset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  64 65 66 61 75 6C 74 2D 63 68 61 72 61 63 74 65  default-characte
00000010  72 2D 73 65 74 3D 75 74 66 38 0A 64 65 66 61 75  r-set=utf8.defau
00000020  6C 74 2D 63 6F 6C 6C 61 74 69 6F 6E 3D 75 74 66  lt-collation=utf
00000030  38 5F 67 65 6E 65 72 61 6C 5F 63 69 0A           8_general_ci.

- Luego, puedes abrir también los ficheros que corresponden a cada una de tus tablas, en mi caso: persons.idb, buscando un poco puedes encontrar como se ha almacenado tus datos en bruto, por ejemplo en mi caso:

CitarOffset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

0000C090  00 C3 81 6C 76 61 72 6F 44 C3 AD 61 7A 06 05 00  .Ã.lvaroDÃ.az...
0000C0A0  00 00 18 00 24 80 00 00 02 00 00 00 00 00 00 80  ....$€.........€
0000C0B0  00 00 00 00 00 00 49 6E C3 A9 73 4C C3 B3 70 65  ......InésLópe
0000C0C0  7A 07 05 00 00 00 20 00 25 80 00 00 03 00 00 00  z..... .%€......
0000C0D0  00 00 00 80 00 00 00 00 00 00 52 65 6E C3 A9 47  ...€......RenéG
0000C0E0  75 7A 6D C3 A1 6E 07 07 00 00 00 28 FF 82 80 00  uzmán.....(ÿ,€.
0000C0F0  00 04 00 00 00 00 00 00 80 00 00 00 00 00 00 53  ........€......S
0000C100  61 6C 6F 6D C3 A9 47 61 72 63 C3 AD 61 00 00 00  aloméGarcÃ.a...

- Esos son los datos para:
CitarÁlvaro Díaz
Inés López
René Guzmán
Salomé García

MinusFour

#7
Otra cosa que tienes que estar seguro que este correcto es el charset de la base de datos. No lo confundas con el tipo de colación, la base de datos también tiene que estar en UTF-8.

Me equivoco, si la colación es UTF8, tu base de datos también debería tiene ese charset. Es casi seguro que sea un problema con la conexión.

No probaste con el primer código que te di?
Código (php) [Seleccionar]

echo mysqli_character_set_name($mysqli); //$mysqli es tu conexión SQL


¿Esto te regresa utf8?

Leguim

#8
Sería algo así, si ya se soy algo duro para entender  :xD

Código (php) [Seleccionar]

function Conexion($user, $pass)
{
try
{
$con = new PDO('mysql:host=localhost;dbname=basedatos', $user, $pass);
$con->character_set_name();
return $con;
}
catch(PDOException $e)
{
return $e->getMessage();
}
}


Porque la manera en la que lo puse da errores, también probé tal y como me lo pasaste mysql_chararacter_set_name($mysql);
pero da el error de que la función no existe (la variable $mysql la adapte por supuesto)

EDIT: recien me di cuenta que que era mysqli_ y no mysql_ viendo el manual de php me di cuenta nose como no pude ver la i en la función que mandaste, igual ahora me da un warning

"mysqli_character_set_name expects parameter 1 to be mysqli, object given in on line 1301"

Código (php) [Seleccionar]

        function Buscar_Usuarios_Ajax($search)
{
$con = Conexion("root", "");
echo mysqli_character_set_name($con);
$consulta = $con->prepare("SELECT id_user, name, surname, avatar FROM usuarios WHERE CONCAT(name, ' ', surname) LIKE '%".($search)."%' ORDER BY CONCAT(name, ' ', surname) ASC LIMIT 6");
$consulta->execute();
$resultados = $consulta->fetchAll();

return $resultados;
}

MinusFour

#9
Cita de: MiguelCanellas en 27 Agosto 2019, 02:04 AM
Sería algo así, si ya se soy algo duro para entender  :xD

Código (php) [Seleccionar]

function Conexion($user, $pass)
{
try
{
$con = new PDO('mysql:host=localhost;dbname=basedatos', $user, $pass);
$con->character_set_name();
return $con;
}
catch(PDOException $e)
{
return $e->getMessage();
}
}


Porque la manera en la que lo puse da errores, también probé tal y como me lo pasaste mysql_chararacter_set_name($mysql);
pero da el error de que la función no existe (la variable $mysql la adapte por supuesto)

Perdón tenía el nombre mal no es mysql, es mysqli. ¿Estás usando PDO en lugar de MySQLi? Tenía entendido que estabas usando MySQLi. Bueno, de todas formas, si has puesto la configuración que he puesto antes, no debería importar que driver estes utilizando.

PDO es más genérico, si quieres revisar que charset estás utilizando desde PDO puedes hacer una query desde PDO, la query es:

Código (sql) [Seleccionar]

SELECT @@character_set_client;


Si el charset de la conexión es el culpable, vas a tener que re insertar la información correctamente. Pero es importante que revises el charset que usa tu conexión antes de re insertar.