Я меняю название, потому что я не знал о специальных сломанных персонажах, которые вызывали у меня проблемы, поэтому вопрос выглядел как дубликат.
Как преобразовать объекты HTML, символьные ссылки типа & # [0-9] +; и & # x [a-fA-F0-9] + ;, недопустимые ссылки на символы – и недопустимые символы Windows chr (151) на их эквиваленты UTF-8?
В основном, как очистить очень плохой текст переменной кодировки и сохранить его как UTF-8?
оригинальный вопрос ниже
Преобразовать & # [0-9] +; и & # x [a-fA-F0-9] +; ссылки на эквивалентные значения UTF-8?
например
к –
как это делает браузер, но с php.
edit: даже нестандартные, которые делают окна, но браузеры.
Отвечая на мой вопрос с решением, которое я использовал в конце
Мне нужно было заменить html-сущности и десятичные и шестнадцатеричные ссылки на символы, которые выглядели так: ‚
и ‚
и &#emdash;
к их эквивалентам UTF-8, как обычный браузер, и преобразовать текст в UTF-8.
Проблема заключалась в том, что часто были ссылки, которые находились в диапазоне 130-150 и x82-x9F, которые, как выяснилось, были недопустимыми символами слова Windows, которые люди используют с текстом ASCII для специальных символов, таких как emdash, которые не поддерживаются php's html_entity_decode.
Вы могли бы подумать, что эти недопустимые символы не будут работать в браузерах, но похоже, что браузеры сделали молчаливое недокументированное соглашение, чтобы исправить эти символы и правильно отобразить их.
При попытке исправить эти ссылки я также выяснил, что используются фактические символы, такие как <?php echo chr(151);?>
, Которые, вероятно, были непосредственно скопированы из слова, и вызовут всевозможные проблемы, поэтому мне нужно было их тоже исправлять.
Большинство ответов, которые я нашел в отношении кодировок, не упоминают, так это то, что решение проблемы с кодированием часто зависит от используемой кодировки. Вот пример:
Недопустимый символ окна chr(151)
будет работать с кодированным текстом «ISO-8859-1», а Джош Б упоминает в соответствии с предложением Юкки Корпелас, что вы должны их исправить следующим образом:
$str = str_replace(chr(151),'--',$str);
То, что он делает, заменяет символ Windows на безопасную альтернативу ASCII, но, зная, что текст будет сохранен в UTF-8, я не хотел потерять исходные символы. Хотя их изменение не было возможным, поскольку ASCII не поддерживает нужный символ Unicode:
$str = str_replace(chr(151),chr(8218),$str);
Итак, вместо этого я заменил символ на ссылку html (в то время как $ str был «ISO-8859-1» закодирован:
$str = str_replace(chr(151),'‚'),$str);
Затем я меняю кодировку
$str = iconv('ISO-8859-1', 'UTF-8//IGNORE', $str);//convert to UTF-8
И, наконец, я превращаю все сущности и символьные ссылки в чистый UTF-8 с помощью моей функции «html_character_reference_decode», которая в значительной степени основана на решении Gumbos , которая также исправляет плохие ссылки на окна, но использует только preg_replace_callback
чтобы переходить с неправильными символами Windows.
function fix_char_mapping($match){ if (strtolower($match[1][0]) === "x") { $codepoint = intval(substr($match[1], 1), 16); } else { $codepoint = intval($match[1], 10); } $mapping = array(8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,142,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,158,376); $codepoint = $mapping[$codepoint-130]; return '&#'.$codepoint.';'; } function html_character_reference_decode($string, $encoding='UTF-8', $fixMappingBug=true){ if($fixMappingBug){ $string = preg_replace_callback('/&#(1[3-5][0-9]|x8[2-9a-f]|x9[0-9a-f]);/i','fix_char_mapping',$string); } return html_entity_decode($string, ENT_QUOTES, 'UTF-8'); } header('Content-Type: text; charset=UTF-8'); echo html_character_reference_decode('dash — and another dash — text ו and more tests נוף ');
Поэтому, если ваш текст кодируется «ISO-8859-1», полное решение выглядит так:
<?php header('Content-Type: text/plain; charset=utf-8'); ini_set("default_charset", 'utf-8'); error_reporting(-1); $encoding = 'ISO-8859-1';//put encoding here $str = 'Ÿ œ bad–string: '.chr(151);//ASCII if($encoding==='ISO-8859-1'){ //fix bad windows characters $badchars = array( '‚'=>chr('130'),//',' baseline single quote 'ƒ'=>chr('131'),//'NLG' florin '„'=>chr('132'),//'"' baseline double quote '…'=>chr('133'),//'...' ellipsis '†'=>chr('134'),//'**' dagger (a second footnote) '‡'=>chr('135'),//'***' double dagger (a third footnote) 'ˆ'=>chr('136'),//'^' circumflex accent '‰'=>chr('137'),//'o/oo' permile 'Š'=>chr('138'),//'Sh' S Hacek '‹'=>chr('139'),//'<' left single guillemet 'Œ'=>chr('140'),//'OE' OE ligature '‘'=>chr('145'),//"'" left single quote '’'=>chr('146'),//"'" right single quote '“'=>chr('147'),//'"' left double quote '”'=>chr('148'),//'"' right double quote '•'=>chr('149'),//'-' bullet '–'=>chr('150'),//'-' endash '—'=>chr('151'),//'--' emdash '˜'=>chr('152'),//'~' tilde accent '™'=>chr('153'),//'(TM)' trademark ligature 'š'=>chr('154'),//'sh' s Hacek '›'=>chr('155'),//'>' right single guillemet 'œ'=>chr('156'),//'oe' oe ligature 'Ÿ'=>chr('159'),//'Y' Y Dieresis ); $str = str_replace(array_values($badchars),array_keys($badchars),$str); $str = iconv('ISO-8859-1', 'UTF-8//IGNORE', $str);//convert to UTF-8 $str = html_character_reference_decode($str);//fixes bad entities above echo $str;die; }
Он был протестирован с широким спектром ситуаций и выглядит так, как будто он работает.
Один надежный способ проверить наличие плохих символов или «плохо сформированный UTF-8» состоял в том, чтобы использовать значок, он медленный, но был более надежным, чем использование preg_match в моих тестах:
$cleaned = iconv('UTF-8','UTF-8//IGNORE',$str); if ($cleaned!==$str){ //contains bad characters, use cleaned version where the bad characters were stripped $str = $cleaned; }
Это было в значительной степени лучшим, о чем я мог подумать, поскольку не нашел разумного способа найти и заменить символы плохого окна в тексте UTF-8, позвольте мне объяснить, почему.
позволяет взять строку с вполне допустимым символом Unicode $str = "—".chr(151);
и плохие окна emdash.
Я не знаю, какие плохие символы Windows могут присутствовать в строке UTF-8, только чтобы они присутствовали.
Использование str_replace
для исправления плохого символа окна chr(148)
(правая двойная кавычка) в приведенной выше правильной строке emdash, которая даже не содержит никаких двойных кавычек, приведет к символу scrambeled, сначала я думал, что str_replace
может не быть многобайтным и попытался использовать mb_eregi_replace
но проблема была такой же.
Комментарии на веб-сайте php и stackoverflow str_replace
о том, что str_replace
безопасен двоично и отлично работает с хорошо сформированным текстом UTF-8 из-за того, что был разработан UTF-8.
Он показывает, что символ «плохой» окна chr(148)
состоит из следующих битов « 10010100 », а символ (emdash) ( http://www.fileformat.info/info/unicode/char/2014/index.htm ), который согласно веб-сайту fileformat состоит из 3 байтов: «11100010: 10000000: 10010100 »
Обратите внимание, что биты в последнем байте в абсолютно правильном символе UTF-8 соответствуют битам в двойной двойной кавычки плохих окон, поэтому str_replace
просто заменяет последний байт, нарушая символ UTF-8. Эта проблема возникает с большим количеством символов в Юникоде и, например, скремблирует много символов в русском тексте.
Это не может случиться с текстом ASCII, потому что каждый символ всегда состоит из одного байта.
Поэтому, когда вы получаете строку UTF-8, содержащую любое количество многобайтовых символов, вы больше не можете безопасно исправлять неправильные символы Windows, и единственным решением, которое я нашел, было удаление их с помощью iconv
$str = iconv('UTF-8', 'UTF-8//IGNORE', $str);
Хотя вы всегда можете заменить действительные символы юникода, содержащие байты плохих символов, на их закодированные копии, затем заменить плохие символы, а затем декодировать хорошие символы, тем самым сохраняя все 🙂
как это:
11100010:10000000:10010100
с кодировкой типа —
10010100
на правильную em dash —
—
назад к 11100010:10000000:10010100
Но для этого нужно записать каждый многобайтовый символ, содержащий байты, которые соответствуют плохим символам.
Связано: Какая разница между EM Dash # 151; и # 8212 ;?
Это намного сложнее, чем я думал, когда я написал свой ответ.
Gumbo обновил свой ответ на очень похожий вопрос, поэтому просто прочитайте:
Как преобразовать ссылки на символы HTML (& # x5E3;) в обычный UTF-8?