Detecte a codificação e crie tudo UTF-8


304

Estou lendo muitos textos de vários feeds RSS e inserindo-os no meu banco de dados.

Obviamente, existem várias codificações de caracteres diferentes usadas nos feeds, por exemplo, UTF-8 e ISO 8859-1.

Infelizmente, às vezes há problemas com a codificação dos textos. Exemplo:

  1. O "ß" em "Fußball" deve ficar assim no meu banco de dados: "Â". Se for um "Â", é exibido corretamente.

  2. Às vezes, o "ß" em "Fußball" fica assim no meu banco de dados: "ÃÂ". Então é exibido incorretamente, é claro.

  3. Em outros casos, o "ß" é salvo como um "ß" - portanto, sem nenhuma alteração. Então também é exibido incorretamente.

O que posso fazer para evitar os casos 2 e 3?

Como posso fazer tudo da mesma codificação, de preferência UTF-8? Quando devo usar utf8_encode(), quando devo usar utf8_decode()(está claro qual é o efeito, mas quando devo usar as funções?) E quando devo fazer nada com a entrada?

Como faço para tornar tudo a mesma codificação? Talvez com a função mb_detect_encoding()? Posso escrever uma função para isso? Então, meus problemas são:

  1. Como descubro qual codificação o texto usa?
  2. Como o converto para UTF-8 - qualquer que seja a codificação antiga?

Uma função como esta funcionaria?

function correct_encoding($text) {
    $current_encoding = mb_detect_encoding($text, 'auto');
    $text = iconv($current_encoding, 'UTF-8', $text);
    return $text;
}

Eu testei, mas não funciona. O que há de errado com isso?


36
"O" ß "em" Fußball "deve ficar assim no meu banco de dados:" Â ".". Não, deve parecer com ß. Verifique se o agrupamento e a conexão estão configurados corretamente. Caso contrário, a classificação e a pesquisa serão interrompidas para você.
Ricos Bradshaw

5
Seu banco de dados está mal configurado. Se você deseja armazenar conteúdo Unicode, basta configurá-lo para isso. Portanto, em vez de tentar solucionar o problema no seu código PHP, você deve primeiro corrigir o banco de dados.
Dolmen

2
USE: $ from = mb_detect_encoding ($ text); $ text = mb_convert_encoding ($ text, 'UTF-8', $ from);
Informate.it

Respostas:


363

Se você aplicar utf8_encode()a uma string já UTF-8, ela retornará uma saída UTF-8 ilegível.

Eu criei uma função que aborda todos esses problemas. É chamado Encoding::toUTF8().

Você não precisa saber qual é a codificação de suas strings. Pode ser Latin1 ( ISO 8859-1) , Windows-1252 ou UTF-8, ou a cadeia pode ter uma mistura deles. Encoding::toUTF8()irá converter tudo para UTF-8.

Fiz isso porque um serviço estava me fornecendo um feed de dados todo bagunçado, misturando UTF-8 e Latin1 na mesma string.

Uso:

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

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

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

Baixar:

https://github.com/neitanod/forceutf8

Incluí outra função, Encoding::fixUFT8()que corrigirá todas as seqüências UTF-8 que parecem ilegíveis.

Uso:

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

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

Exemplos:

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

irá produzir:

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

Transformei a função ( forceUTF8) em uma família de funções estáticas em uma classe chamada Encoding. A nova função é Encoding::toUTF8().


1
Bem, se você olhar para o código, o fixUTF8 simplesmente chama o forceUTF8 uma e outra vez até que a string seja retornada inalterada. Uma chamada para fixUTF8 () leva pelo menos duas vezes o tempo de uma chamada para forceUTF8 (), portanto, é muito menos eficiente. Eu criei o fixUTF8 () apenas para criar um programa de linha de comando que corrigisse arquivos "corrompidos por codificação", mas em um ambiente ativo raramente é necessário.
Sebastián Grignoli 27/08/10

3
Como isso converte caracteres não UTF8 em UTF8, sem saber em que codificação estão os caracteres inválidos?
philfreo

4
Supõe ISO-8859-1, a resposta já diz isso. A única diferença entre forceUTF8 () e utf8_encode () é que forceUTF8 () reconhece caracteres UTF8 e os mantém inalterados.
Sebastián Grignoli

28
"Você não precisa saber qual é a codificação de suas cordas." Eu discordo muito. Adivinhar e tentar pode funcionar, mas você sempre, mais cedo ou mais tarde, encontra casos extremos onde não funciona.
deceze

4
Eu concordo totalmente. Na verdade, eu não queria estado que, como regra geral, apenas explicar que esta classe pode ajudá-lo a se essa é a situação acontecer de você encontrar-se em.
Sebastián Grignoli

74

Você primeiro precisa detectar qual codificação foi usada. Ao analisar feeds RSS (provavelmente via HTTP), você deve ler a codificação no charsetparâmetro do Content-Typecampo do cabeçalho HTTP . Se não estiver presente, leia a codificação no encodingatributo da instrução de processamento XML . Se isso também estiver faltando, use UTF-8 conforme definido na especificação .


Editar    Aqui está o que eu provavelmente faria:

Eu usaria cURL para enviar e buscar a resposta. Isso permite definir campos de cabeçalho específicos e buscar também o cabeçalho de resposta. Após buscar a resposta, você deve analisar a resposta HTTP e dividi-la em cabeçalho e corpo. O cabeçalho deve conter o Content-Typecampo de cabeçalho que contém o tipo MIME e (espero) o charsetparâmetro com a codificação / conjunto de caracteres também. Caso contrário, analisaremos o XML PI quanto à presença do encodingatributo e obteremos a codificação a partir daí. Se isso também estiver faltando, as especificações XML definem o uso de UTF-8 como codificação.

$url = 'http://www.lr-online.de/storage/rss/rss/sport.xml';

$accept = array(
    'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
    'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
    'Accept: '.implode(', ', $accept['type']),
    'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
    // error fetching the response
} else {
    $offset = strpos($response, "\r\n\r\n");
    $header = substr($response, 0, $offset);
    if (!$header || !preg_match('/^Content-Type:\s+([^;]+)(?:;\s*charset=(.*))?/im', $header, $match)) {
        // error parsing the response
    } else {
        if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
            // type not accepted
        }
        $encoding = trim($match[2], '"\'');
    }
    if (!$encoding) {
        $body = substr($response, $offset + 4);
        if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $body, $match)) {
            $encoding = trim($match[1], '"\'');
        }
    }
    if (!$encoding) {
        $encoding = 'utf-8';
    } else {
        if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
            // encoding not accepted
        }
        if ($encoding != 'utf-8') {
            $body = mb_convert_encoding($body, 'utf-8', $encoding);
        }
    }
    $simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
    if (!$simpleXML) {
        // parse error
    } else {
        echo $simpleXML->asXML();
    }
}

Obrigado. Isso seria fácil. Mas isso realmente funcionaria? Geralmente, existem codificações incorretas fornecidas nos cabeçalhos HTTP ou nos atributos do XML.
caw

25
Novamente: Esse não é o seu problema. Foram estabelecidos padrões para evitar tais problemas. Se outros não os seguirem, é problema deles, não seu.
Gumbo

Ok, acho que você finalmente me convenceu agora. :)
caw

Obrigado pelo código. Mas por que não simplesmente usar isso? paste.bradleygill.com/index.php?paste_id=9651 Seu código é muito mais complexo, o que é melhor com ele?
caw

Bem, primeiro você está fazendo duas solicitações, uma para o cabeçalho HTTP e outra para os dados. Em segundo lugar, você está olhando para qualquer aparência de charset=e encoding=e não apenas nas posições apropriadas. E terceiro, você não está verificando se a codificação declarada é aceita.
Gumbo

39

Detectar a codificação é difícil.

mb_detect_encodingfunciona adivinhando, com base em vários candidatos aprovados. Em algumas codificações, determinadas seqüências de bytes são inválidas e, portanto, podem distinguir entre vários candidatos. Infelizmente, existem muitas codificações em que os mesmos bytes são válidos (mas diferentes). Nesses casos, não há como determinar a codificação; Você pode implementar sua própria lógica para fazer suposições nesses casos. Por exemplo, é provável que os dados provenientes de um site japonês tenham uma codificação japonesa.

Contanto que você só lidar com idiomas da Europa Ocidental, as três principais codificações a considerar são utf-8, iso-8859-1e cp-1252. Como esses padrões são padrão para muitas plataformas, eles também são os mais propensos a serem relatados incorretamente. Por exemplo. se as pessoas usam codificações diferentes, é provável que sejam francas a respeito, pois caso contrário, seu software quebraria com muita frequência. Portanto, uma boa estratégia é confiar no provedor, a menos que a codificação seja relatada como uma dessas três. Você ainda deve verificar duas vezes se é realmente válido, usando mb_check_encoding(observe que válido não é o mesmo que ser - a mesma entrada pode ser válida para muitas codificações). Se for um desses, você poderá usarmb_detect_encodingpara distinguir entre eles. Felizmente isso é bastante determinístico; Você só precisa usar a sequência de detecção adequada, que é UTF-8,ISO-8859-1,WINDOWS-1252.

Depois de detectar a codificação, você precisa convertê-la em sua representação interna ( UTF-8é a única opção sensata). A função é utf8_encodetransformada ISO-8859-1em UTF-8, portanto, pode ser usada apenas para esse tipo de entrada específico. Para outras codificações, use mb_convert_encoding.


Muito obrigado! O que é melhor: mb-convert-encoding () ou iconv ()? Não sei quais são as diferenças. Sim, só preciso analisar os idiomas da Europa Ocidental, principalmente inglês, alemão e francês.
caw

7
Acabei de ver: mb-detect-encoding () é inútil. Ele suporta apenas UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS e ISO-2022-JP. Os mais importantes para mim, ISO-8859-1 e WINDOWS-1252, não são suportados. Portanto, não posso usar o mb-detect-encoding ().
caw

1
Meu bem, você está certo. Já faz um tempo desde que eu o usei. Você precisará escrever seu próprio código de detecção ou usar um utilitário externo. O UTF-8 pode ser determinado com confiabilidade, porque suas seqüências de escape são bastante características. wp-1252 e iso-8859-1 podem ser distinguidos porque wp-1252 pode conter bytes ilegais na iso-8859-1. Use a Wikipedia para obter os detalhes, ou consulte a seção de comentários do php.net, sob várias funções relacionadas ao charset.
31309 troelskn

Eu acho que você pode distinguir as diferentes codificações quando olha para as formas em que o especial canta: O "ß" alemão emerge de diferentes formas: às vezes "Ÿ", às vezes "ƒ" e às vezes "ß". Por quê?
caw

Sim, mas você precisa conhecer o conteúdo da string antes de compará-la, e isso derrota o objetivo em primeiro lugar. O alemão ß aparece de maneira diferente porque possui valores diferentes em codificações diferentes. Alguns caracteres são representados da mesma maneira em diferentes codificações (por exemplo, todos os caracteres no conjunto de caracteres ascii são codificados da mesma maneira em utf-8, iso-8859- * e wp-1252), desde que você use apenas esses personagens, todos têm a mesma aparência. É por isso que às vezes são chamados de compatíveis com ASCII.
31309 troelskn

14

Uma maneira muito boa de implementar uma isUTF8função-pode ser encontrada em php.net :

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}

16
Infelizmente, isso só funciona quando a string consiste apenas em caracteres incluídos na ISO-8859-1. Mas isso poderia funcionar: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Christian Davén

@ Christian: De fato, é isso que os autores do MySQL de alto desempenho também recomendam.
Alix Axel

1
Não funciona corretamente: echo (int) isUTF8 ('z'); # 1 eco (int) éUTF8 (NULL); # 1
Yousha Aleayoub

1
Embora não seja perfeito, acho que essa é uma boa maneira de implementar uma verificação superficial UTF-8.
Mateng

1
mb_check_encoding($string, 'UTF-8')
deceze

13

Esta folha de dicas lista algumas advertências comuns relacionadas ao manuseio de UTF-8 no PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Essa função de detecção de caracteres multibyte em uma string também pode ser útil ( fonte ):


function detectUTF8($string)
{
    return preg_match('%(?:
        [\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);
}


2
Eu acho que isso não funciona corretamente: echo detectUTF8 ('3٣3'); # 1
Yousha Aleayoub

10

Um pouco de atenção. Você disse que o "ß" deve ser exibido como "Â" no seu banco de dados.

Provavelmente porque você está usando um banco de dados com codificação de caracteres Latin-1 ou, possivelmente, sua conexão PHP-MySQL está incorreta, isto é, P acredita que seu MySQL está configurado para usar UTF-8, para que ele envie dados como UTF-8 , mas seu MySQL acredita que o PHP está enviando dados codificados como ISO 8859-1, portanto, mais uma vez, você pode tentar codificar seus dados enviados como UTF-8, causando esse tipo de problema.

Dê uma olhada no mysql_set_charset . Isso pode ajudá-lo.


4

Sua codificação parece que você codificou em UTF-8 duas vezes ; isto é, de alguma outra codificação, em UTF-8 e novamente em UTF-8. Como se você tivesse ISO 8859-1, convertido de ISO 8859-1 para UTF-8 e tratado a nova string como ISO 8859-1 para outra conversão em UTF-8.

Aqui estão alguns pseudocódigo do que você fez:

$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);

Você deveria tentar:

  1. detectar a codificação usando mb_detect_encoding()ou o que você quiser usar
  2. se for UTF-8, converta para ISO 8859-1 e repita a etapa 1
  3. finalmente, converta novamente em UTF-8

Isso pressupõe que, na conversão "intermediária", você utilizou a ISO 8859-1. Se você usou o Windows 1252, converta-o para o Windows 1252 (latin1). A codificação original da fonte não é importante; o que você usou em falha, segunda conversão é.

Este é o meu palpite sobre o que aconteceu; há muito pouco que você poderia ter feito para obter quatro bytes no lugar de um byte ASCII estendido.

O idioma alemão também usa ISO 8859-2 e Windows-1250 (latino-2).


3

O interessante mb_detect_encodinge mb_convert_encodingé que a ordem das codificações sugeridas é importante:

// $input is actually UTF-8

mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
// ISO-8859-9 (WRONG!)

mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
// UTF-8 (OK)

Portanto, convém usar uma ordem específica ao especificar as codificações esperadas. Ainda assim, lembre-se de que isso não é infalível.


2
Isso acontece porque, na prática, a ISO-8859-9 aceita qualquer entrada binária. O mesmo vale para Windows-1252 e amigos. Você deve primeiro testar as codificações que podem falhar em aceitar a entrada.
Mikko Rantalainen

@MikkoRantalainen, sim, acho que essa parte dos documentos diz algo semelhante: php.net/manual/en/function.mb-detect-order.php#example-2985 #
Halil Özgür

Considerando que a especificação HTML do WHATWG define o Windows 1252 como a codificação padrão, deve ser bastante seguro assumir isso if ($input_is_not_UTF8) $input_is_windows1252 = true;. Veja também: html.spec.whatwg.org/multipage/…
Mikko Rantalainen

3

Você precisa testar o conjunto de caracteres na entrada, pois as respostas podem vir codificadas com codificações diferentes.

Forço todo o conteúdo a ser enviado ao UTF-8, fazendo a detecção e a tradução usando a seguinte função:

function fixRequestCharset()
{
  $ref = array(&$_GET, &$_POST, &$_REQUEST);
  foreach ($ref as &$var)
  {
    foreach ($var as $key => $val)
    {
      $encoding = mb_detect_encoding($var[$key], mb_detect_order(), true);
      if (!$encoding)
        continue;
      if (strcasecmp($encoding, 'UTF-8') != 0)
      {
        $encoding = iconv($encoding, 'UTF-8', $var[$key]);
        if ($encoding === false)
          continue;
        $var[$key] = $encoding;
      }
    }
  }
}

Essa rotina transformará todas as variáveis ​​PHP que vêm do host remoto em UTF-8.

Ou ignore o valor se a codificação não puder ser detectada ou convertida.

Você pode personalizá-lo de acordo com suas necessidades.

Apenas invoque-o antes de usar as variáveis.


qual é o objetivo de usar mb_detect_order () sem passar na lista de codificação?
giorgio79

O objetivo é retornar o conjunto de codificações ordenadas configuradas pelo sistema definidas no php.ini usado. Isso é exigido pelo mb_detect_encoding para preencher o terceiro parâmetro.
Cavila

2

Trabalhar com a codificação de caracteres dos feeds RSS parece ser complicado . Mesmo páginas da Web normais geralmente omitem ou mentem sobre sua codificação.

Portanto, você pode tentar usar a maneira correta de detectar a codificação e voltar a alguma forma de detecção automática (suposição).


Não quero ler a codificação das informações do feed. Portanto, é igual se as informações do feed estiverem incorretas. Gostaria de detectar a codificação do texto.
caw

@ marco92w: Não é problema seu se a codificação declarada estiver errada. Os padrões não foram estabelecidos por diversão.
Gumbo

1
@ Gumbo: mas se você trabalha no mundo real, precisa lidar com coisas como codificações declaradas incorretas. O problema é que é muito difícil adivinhar (corretamente) a codificação apenas a partir de algum texto. Os padrões são maravilhosos, mas muitas (a maioria?) Das páginas / feeds existentes não os cumprem.
219 Kevin Orourke

@ Kevin ORourke: Exatamente, certo. Esse é meu problema. @ Gumbo: Sim, o problema é meu. Quero ler os feeds e agregá-los. Portanto, devo corrigir as codificações incorretas.
caw

@ marco92w: Mas você não pode corrigir a codificação se não souber a codificação correta e a atual. E isso é o que o charset/ encodingdeclaração se por: descrever a codificação dos dados é codificado em.
Gumbo

2

Sei que essa é uma pergunta mais antiga, mas acho que uma resposta útil nunca é demais. Eu estava tendo problemas com minha codificação entre um aplicativo de desktop, SQLite e variáveis ​​GET / POST. Alguns estariam em UTF-8, outros em ASCII, e basicamente tudo estragaria quando personagens estrangeiros se envolvessem.

Aqui está a minha solução. Ele limpa seu GET / POST / REQUEST (eu omiti os cookies, mas você pode adicioná-los, se desejar) em cada carregamento da página antes do processamento. Funciona bem em um cabeçalho. O PHP lançará avisos se não puder detectar a codificação de origem automaticamente, portanto, esses avisos serão suprimidos com @ 's.

//Convert everything in our vars to UTF-8 for playing nice with the database...
//Use some auto detection here to help us not double-encode...
//Suppress possible warnings with @'s for when encoding cannot be detected
try
{
    $process = array(&$_GET, &$_POST, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
                $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
            } else {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
            }
        }
    }
    unset($process);
}
catch(Exception $ex){}

Obrigado pela resposta, jocull. A função mb_convert_encoding () é o que já tivemos aqui, certo? ;) Portanto, a única coisa nova na sua resposta são os loops para alterar a codificação em todas as variáveis.
caw

2

Eu estava procurando soluções para codificação desde idades , e esta página é provavelmente a conclusão de anos de pesquisa! Testei algumas das sugestões que você mencionou e aqui estão minhas anotações:

Esta é a minha string de teste:

esta é uma string "wròng wrìtten", mas eu preciso de mais 'quartos especiais' para vê-los, convertidos por conexão !! & é isso aí!

Eu faço um INSERT para salvar essa sequência em um banco de dados em um campo definido como utf8_general_ci

O conjunto de caracteres da minha página é UTF-8.

Se eu fizer um INSERT assim, no meu banco de dados, tenho alguns personagens provavelmente vindos de Marte ...

Então, eu preciso convertê-los em algum UTF-8 "saudável". Eu tentei utf8_encode(), mas os caracteres alienígenas ainda estavam invadindo meu banco de dados ...

Então, tentei usar a função forceUTF8postada no número 8, mas no banco de dados a string salva é assim:

esta é uma string "wr wr wrttten", mas nunca usei um pêra especial para vê-lo, convertido por associação !! & é isso aí!

Então, coletando mais algumas informações nesta página e mesclando-as com outras informações em outras páginas, resolvi meu problema com esta solução:

$finallyIDidIt = mb_convert_encoding(
  $string,
  mysql_client_encoding($resourceID),
  mb_detect_encoding($string)
);

Agora, no meu banco de dados, tenho minha string com a codificação correta.

NOTA: Somente observe para cuidar está em funcionamento mysql_client_encoding! Você precisa estar conectado ao banco de dados, porque esta função deseja um ID de recurso como parâmetro.

Mas bem, eu apenas faço essa recodificação antes do meu INSERT, portanto, para mim, não é um problema.


1
Por que você não usa apenas a UTF-8codificação do cliente para mysql? Não precisaria de conversão manual dessa maneira
Esailija

2

É simples: quando você obtém algo que não é UTF-8, deve codificá- lo para UTF-8.

Portanto, quando você está buscando um determinado feed que seja ISO 8859-1, analise-o utf8_encode.

No entanto, se você está buscando um feed UTF-8, não precisa fazer nada.


Obrigado! OK, posso descobrir como o feed é codificado usando mb-detect-encoding (), certo? Mas o que posso fazer se o feed for ASCII? utf8-encode () é apenas para ISO-8859-1 a UTF-8, não é?
caw

ASCII é um subconjunto de ISO-8859-1 e UTF-8, portanto, usando utf8-codificar () não deve fazer uma mudança - se é realmente apenas ASCII
Michael Borgwardt

Então, eu sempre posso usar utf8_encode se não for UTF-8? Isso seria realmente fácil. O texto que era ASCII de acordo com mb-detect-encoding () continha "& # 228;". Este é um caractere ASCII? Ou é HTML?
caw

Isso é HTML. Na verdade, isso é codificado; portanto, quando você o imprime em uma determinada página, mostra-se ok. Se você quiser, pode primeiro ut8_encode () e depois html_entity_decode ().
265/09 Seb

1
O caractere ß é codificado em UTF-8 com a sequência de bytes 0xC39F. Interpretada no Windows-1252, essa sequência representa os dois caracteres  (0xC3) e Ÿ (0x9F). E se você codificar essa sequência de bytes novamente com UTF-8, obterá 0xC383 0xC29F o que representa ß no Windows-1252. Portanto, seu erro é manipular esses dados codificados em UTF-8 como algo com uma codificação diferente de UTF-8. Que essa sequência de bytes seja apresentada como o personagem que você está vendo é apenas uma questão de interpretação. Se você usar outra codificação / conjunto de caracteres, provavelmente verá outros caracteres.
Gumbo

1

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

ou

echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");

Realmente não sei quais são os resultados, mas sugiro que você pegue alguns de seus feeds com codificações diferentes e tente se mb_detect_encodingfunciona ou não.

update
auto é a abreviação de "ASCII, JIS, UTF-8, EUC-JP, SJIS". retorna o conjunto de caracteres detectado, que você pode usar para converter a string em utf-8 com iconv .

<?php
function convertToUTF8($str) {
    $enc = mb_detect_encoding($str);

    if ($enc && $enc != 'UTF-8') {
        return iconv($enc, 'UTF-8', $str);
    } else {
        return $str;
    }
}
?>

Eu não testei, então não há garantia. e talvez haja uma maneira mais simples.


Obrigado. Qual é a diferença entre 'auto' e 'UTF-8, ASCII, ISO-8859-1' como o segundo argumento? 'Auto' apresenta mais codificações? Então seria melhor usar 'auto', não seria? Se realmente funcionar sem erros, devo alterar apenas "ASCII" ou "ISO-8859-1" para "UTF-8". Quão?
caw

2
Sua função não funciona bem em todos os casos. Às vezes eu recebo um erro: Aviso: iconv (): Detectado um caractere ilegal na cadeia de entrada em ...
caw

1

@harpax que funcionou para mim. No meu caso, isso é bom o suficiente:

if (isUTF8($str)) { 
    echo $str; 
}
else
{
    echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
}

0

Depois de ordenar seus scripts php, não esqueça de dizer ao mysql qual charset você está passando e gostaria de receber.

Exemplo: definir conjunto de caracteres utf8

A passagem de dados utf8 para uma tabela latin1 em uma sessão de E / S latin1 fornece esses pés de pássaro desagradáveis. Vejo isso todos os dias nas lojas de comércio eletrônico. Voltar e quarta, pode parecer certo. Mas o phpmyadmin mostrará a verdade. Ao dizer ao mysql que conjunto de caracteres você está passando, ele manipulará a conversão de dados mysql para você.

Como recuperar dados mysql embaralhados existentes é outro tópico a ser discutido. :)


0

Esta versão é para o idioma alemão, mas você pode modificar os $ CHARSETS e os $ TESTCHARS

class CharsetDetector
{
private static $CHARSETS = array(
"ISO_8859-1",
"ISO_8859-15",
"CP850"
);
private static $TESTCHARS = array(
"€",
"ä",
"Ä",
"ö",
"Ö",
"ü",
"Ü",
"ß"
);
public static function convert($string)
{
    return self::__iconv($string, self::getCharset($string));
}
public static function getCharset($string)
{
    $normalized = self::__normalize($string);
    if(!strlen($normalized))return "UTF-8";
    $best = "UTF-8";
    $charcountbest = 0;
    foreach (self::$CHARSETS as $charset) {
        $str = self::__iconv($normalized, $charset);
        $charcount = 0;
        $stop   = mb_strlen( $str, "UTF-8");

        for( $idx = 0; $idx < $stop; $idx++)
        {
            $char = mb_substr( $str, $idx, 1, "UTF-8");
            foreach (self::$TESTCHARS as $testchar) {

                if($char == $testchar)
                {

                    $charcount++;
                    break;
                }
            }
        }
        if($charcount>$charcountbest)
        {
            $charcountbest=$charcount;
            $best=$charset;
        }
        //echo $text."<br />";
    }
    return $best;
}
private static function __normalize($str)
{

$len = strlen($str);
$ret = "";
for($i = 0; $i < $len; $i++){
    $c = ord($str[$i]);
    if ($c > 128) {
        if (($c > 247)) $ret .=$str[$i];
        elseif ($c > 239) $bytes = 4;
        elseif ($c > 223) $bytes = 3;
        elseif ($c > 191) $bytes = 2;
        else $ret .=$str[$i];
        if (($i + $bytes) > $len) $ret .=$str[$i];
        $ret2=$str[$i];
        while ($bytes > 1) {
            $i++;
            $b = ord($str[$i]);
            if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
            else $ret2.=$str[$i];
            $bytes--;
        }
    }
}
return $ret; 
}
private static function __iconv($string, $charset)
{
    return iconv ( $charset, "UTF-8" , $string );
}
}


0

Obtenha codificação de cabeçalhos e converta-a em utf-8.

$post_url='http://website.domain';

/// Get headers ////////////////////////////////////////////////////////////
function get_headers_curl($url) 
{ 
    $ch = curl_init(); 

    curl_setopt($ch, CURLOPT_URL,            $url); 
    curl_setopt($ch, CURLOPT_HEADER,         true); 
    curl_setopt($ch, CURLOPT_NOBODY,         true); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_TIMEOUT,        15); 

    $r = curl_exec($ch); 
    return $r; 
}
$the_header = get_headers_curl($post_url);
/// check for redirect /////////////////////////////////////////////////
if (preg_match("/Location:/i", $the_header)) {
    $arr = explode('Location:', $the_header);
    $location = $arr[1];

    $location=explode(chr(10), $location);
    $location = $location[0];

$the_header = get_headers_curl(trim($location));
}
/// Get charset /////////////////////////////////////////////////////////////////////
if (preg_match("/charset=/i", $the_header)) {
    $arr = explode('charset=', $the_header);
    $charset = $arr[1];

    $charset=explode(chr(10), $charset);
    $charset = $charset[0];
    }
///////////////////////////////////////////////////////////////////////////////
// echo $charset;

if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }

0

Ÿé Mojibake para ß. No seu banco de dados, você pode ter hexadecimal

DF if the column is "latin1",
C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
C383C5B8 if double-encoded into a utf8 column

Você não deve usar nenhuma função de codificação / decodificação no PHP; em vez disso, você deve configurar o banco de dados e a conexão a ele corretamente.

Se o MySQL estiver envolvido, consulte: Problemas com caracteres utf8; o que vejo não é o que guardei


0

Encontre a solução aqui http://deer.org.ua/2009/10/06/1/

class Encoding
{
    /**
     * http://deer.org.ua/2009/10/06/1/
     * @param $string
     * @return null
     */
    public static function detect_encoding($string)
    {
        static $list = ['utf-8', 'windows-1251'];

        foreach ($list as $item) {
            try {
                $sample = iconv($item, $item, $string);
            } catch (\Exception $e) {
                continue;
            }
            if (md5($sample) == md5($string)) {
                return $item;
            }
        }
        return null;
    }
}

$content = file_get_contents($file['tmp_name']);
$encoding = Encoding::detect_encoding($content);
if ($encoding != 'utf-8') {
    $result = iconv($encoding, 'utf-8', $content);
} else {
    $result = $content;
}

Eu acho que @ é uma má decisão e faço algumas alterações na solução do deer.org.ua;


0

A resposta mais votada não funciona. Aqui está o meu e espero que ajude.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}

1
Você tem alguma idéia de por que ou como seus arquivos eram diferentes? Que peças não funcionaram para você? Por exemplo: caracteres alemães maiúsculos não foram convertidos corretamente. Curioso, o que é "GBK"?
precisa saber é o seguinte

-1

Quando você tenta lidar com vários idiomas, como japonês e coreano, pode ter problemas. mb_convert_encoding com o parâmetro 'auto' não funciona bem. Definir mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') não ajuda, pois detecta EUC- * incorretamente.

Concluí que, desde que as strings de entrada venham do HTML, ele deve usar 'charset' em um meta elemento. Eu uso o Simple HTML DOM Parser porque ele suporta HTML inválido.

O trecho abaixo extrai o elemento de título de uma página da web. Se você deseja converter uma página inteira, remova algumas linhas.

<?php
require_once 'simple_html_dom.php';

echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;

function convert_title_to_utf8($contents)
{
    $dom = str_get_html($contents);
    $title = $dom->find('title', 0);
    if (empty($title)) {
        return null;
    }
    $title = $title->plaintext;
    $metas = $dom->find('meta');
    $charset = 'auto';
    foreach ($metas as $meta) {
        if (!empty($meta->charset)) { // html5
            $charset = $meta->charset;
        } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
            $charset = $match[1];
        }
    }
    if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
        $charset = 'auto';
    }
    return mb_convert_encoding($title, 'UTF-8', $charset);
}

-1

Eu tive o mesmo problema com o phpQuery ( ISO-8859-1 em vez de UTF-8 ) e esse truque me ajudou:

$html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingE outras manipulações não tomou qualquer efeito.


-1

Experimente sem 'auto'

Isso é:

mb_detect_encoding($text)

ao invés de:

mb_detect_encoding($text, 'auto')

Mais informações podem ser encontradas aqui: mb_detect_encoding

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.