Преобразование всех типов смарт-котировок с помощью PHP

Я пытаюсь преобразовать все типы умных кавычек в обычные кавычки при работе с текстом. Тем не менее, следующая функция, которую я собрал, по-видимому, не имеет поддержки и правильного дизайна.

Кто-нибудь знает, как правильно преобразовать все цитируемые символы ?

function convert_smart_quotes($string) { $quotes = array( "\xC2\xAB" => '"', // « (U+00AB) in UTF-8 "\xC2\xBB" => '"', // » (U+00BB) in UTF-8 "\xE2\x80\x98" => "'", // ' (U+2018) in UTF-8 "\xE2\x80\x99" => "'", // ' (U+2019) in UTF-8 "\xE2\x80\x9A" => "'", // ‚ (U+201A) in UTF-8 "\xE2\x80\x9B" => "'", // ‛ (U+201B) in UTF-8 "\xE2\x80\x9C" => '"', // “ (U+201C) in UTF-8 "\xE2\x80\x9D" => '"', // ” (U+201D) in UTF-8 "\xE2\x80\x9E" => '"', // „ (U+201E) in UTF-8 "\xE2\x80\x9F" => '"', // ‟ (U+201F) in UTF-8 "\xE2\x80\xB9" => "'", // ‹ (U+2039) in UTF-8 "\xE2\x80\xBA" => "'", // › (U+203A) in UTF-8 ); $string = strtr($string, $quotes); // Version 2 $search = array( chr(145), chr(146), chr(147), chr(148), chr(151) ); $replace = array("'","'",'"','"',' - '); $string = str_replace($search, $replace, $string); // Version 3 $string = str_replace( array('‘','’','“','”'), array("'", "'", '"', '"'), $string ); // Version 4 $search = array( '‘', '’', '“', '”', '—', '–', ); $replace = array("'","'",'"','"',' - ', '-'); $string = str_replace($search, $replace, $string); return $string; } 

Примечание. Этот вопрос представляет собой полный запрос о полной гамме котировок, включая цитируемые здесь цитаты из «Microsoft». Это «дубликат» таким же образом, что спрашивать о всех размерах шин – это «дубликат» запроса на автомобильную шину размер.

Вам нужно что-то вроде этого (предполагая ввод UTF-8 и игнорируя CJK (китайский, японский, корейский)):

 $chr_map = array( // Windows codepage 1252 "\xC2\x82" => "'", // U+0082⇒U+201A single low-9 quotation mark "\xC2\x84" => '"', // U+0084⇒U+201E double low-9 quotation mark "\xC2\x8B" => "'", // U+008B⇒U+2039 single left-pointing angle quotation mark "\xC2\x91" => "'", // U+0091⇒U+2018 left single quotation mark "\xC2\x92" => "'", // U+0092⇒U+2019 right single quotation mark "\xC2\x93" => '"', // U+0093⇒U+201C left double quotation mark "\xC2\x94" => '"', // U+0094⇒U+201D right double quotation mark "\xC2\x9B" => "'", // U+009B⇒U+203A single right-pointing angle quotation mark // Regular Unicode // U+0022 quotation mark (") // U+0027 apostrophe (') "\xC2\xAB" => '"', // U+00AB left-pointing double angle quotation mark "\xC2\xBB" => '"', // U+00BB right-pointing double angle quotation mark "\xE2\x80\x98" => "'", // U+2018 left single quotation mark "\xE2\x80\x99" => "'", // U+2019 right single quotation mark "\xE2\x80\x9A" => "'", // U+201A single low-9 quotation mark "\xE2\x80\x9B" => "'", // U+201B single high-reversed-9 quotation mark "\xE2\x80\x9C" => '"', // U+201C left double quotation mark "\xE2\x80\x9D" => '"', // U+201D right double quotation mark "\xE2\x80\x9E" => '"', // U+201E double low-9 quotation mark "\xE2\x80\x9F" => '"', // U+201F double high-reversed-9 quotation mark "\xE2\x80\xB9" => "'", // U+2039 single left-pointing angle quotation mark "\xE2\x80\xBA" => "'", // U+203A single right-pointing angle quotation mark ); $chr = array_keys ($chr_map); // but: for efficiency you should $rpl = array_values($chr_map); // pre-calculate these two arrays $str = str_replace($chr, $rpl, html_entity_decode($str, ENT_QUOTES, "UTF-8")); 

Здесь идет фон:

Каждый символ Unicode относится к одной «общей категории» , из которых те, которые могут содержать символы кавычек, следующие:

  • Ps "Пунктуация, Open"
  • Pe "Пунктуация, Закрыть"
  • Pi "Пунктуация, начальная цитата (может вести себя как Ps или Pe в зависимости от использования)"
  • Pf "Пунктуация, заключительная цитата (может вести себя как Ps или Pe в зависимости от использования)"
  • Po "Знаки пунктуации, другие"

(эти страницы удобны для проверки того, что вы ничего не пропустили – есть также индекс категорий )

Иногда бывает полезно сопоставить эти категории в регулярном выражении с поддержкой Unicode.

Кроме того, символы Unicode имеют «свойства» , из которых интересует вас Quotation_Mark . К сожалению, они недоступны в регулярном выражении.

В Википедии вы можете найти группу символов с свойством Quotation_Mark . Последняя ссылка – PropList.txt на unicode.org , но это текстовый файл ASCII.

В случае, если вам нужно перевести символы CJK, вам нужно только получить свои кодовые точки, решить их перевод и найти их кодировку UTF-8, например, просмотрев ее в файле fileformat.info (например, для U + 301E: http : //www.fileformat.info/info/unicode/char/301e/index.htm ).

Что касается кодовой страницы Windows 1252: Unicode определяет первые 256 кодовых точек для представления точно таких же символов, как ISO-8859-1 , но ISO-8859-1 часто путают с кодовой страницей Windows 1252 , так что все браузеры отображают диапазон 0x80-0x9F, который является «пустым» в ISO-8859-1 (точнее: содержит контрольные символы), как если бы это была кодовая страница Windows 1252. В таблице на странице Wikipedia перечислены эквиваленты Unicode.

Примечание: strtr() часто медленнее, чем str_replace() . Время это с вашим вводом и вашей версией PHP. Если это достаточно быстро, вы можете напрямую использовать карту, такую ​​как my $chr_map .


Если вы не уверены, что ваш ввод кодируется в кодировке UTF-8, и готовы предположить, что если это не так, то это ISO-8859-1 или кодовая страница Windows 1252, тогда вы можете сделать это раньше всего:

 if ( !preg_match('/^\\X*$/u', $str)) { $str = utf8_encode($str); } 

Предупреждение: это регулярное выражение может в очень редких случаях не обнаруживать кодировку, отличную от UTF-8. Например: "Gruß…"/*CP-1252*/=="Gru\xDF\x85" выглядит как UTF-8 для этого регулярного выражения (U + 07C5 – это цифра N'ko 5). Это регулярное выражение может быть немного улучшено, но, к сожалению, можно показать, что существует НЕТ полностью надежное решение проблемы обнаружения кодирования.


Если вы хотите нормализовать диапазон 0x80-0x9F, который связан с кодовой страницей Windows 1252 с обычными кодовыми точками Unicode, вы можете сделать это (и удалить первую часть $chr_map выше):

 $normalization_map = array( "\xC2\x80" => "\xE2\x82\xAC", // U+20AC Euro sign "\xC2\x82" => "\xE2\x80\x9A", // U+201A single low-9 quotation mark "\xC2\x83" => "\xC6\x92", // U+0192 latin small letter f with hook "\xC2\x84" => "\xE2\x80\x9E", // U+201E double low-9 quotation mark "\xC2\x85" => "\xE2\x80\xA6", // U+2026 horizontal ellipsis "\xC2\x86" => "\xE2\x80\xA0", // U+2020 dagger "\xC2\x87" => "\xE2\x80\xA1", // U+2021 double dagger "\xC2\x88" => "\xCB\x86", // U+02C6 modifier letter circumflex accent "\xC2\x89" => "\xE2\x80\xB0", // U+2030 per mille sign "\xC2\x8A" => "\xC5\xA0", // U+0160 latin capital letter s with caron "\xC2\x8B" => "\xE2\x80\xB9", // U+2039 single left-pointing angle quotation mark "\xC2\x8C" => "\xC5\x92", // U+0152 latin capital ligature oe "\xC2\x8E" => "\xC5\xBD", // U+017D latin capital letter z with caron "\xC2\x91" => "\xE2\x80\x98", // U+2018 left single quotation mark "\xC2\x92" => "\xE2\x80\x99", // U+2019 right single quotation mark "\xC2\x93" => "\xE2\x80\x9C", // U+201C left double quotation mark "\xC2\x94" => "\xE2\x80\x9D", // U+201D right double quotation mark "\xC2\x95" => "\xE2\x80\xA2", // U+2022 bullet "\xC2\x96" => "\xE2\x80\x93", // U+2013 en dash "\xC2\x97" => "\xE2\x80\x94", // U+2014 em dash "\xC2\x98" => "\xCB\x9C", // U+02DC small tilde "\xC2\x99" => "\xE2\x84\xA2", // U+2122 trade mark sign "\xC2\x9A" => "\xC5\xA1", // U+0161 latin small letter s with caron "\xC2\x9B" => "\xE2\x80\xBA", // U+203A single right-pointing angle quotation mark "\xC2\x9C" => "\xC5\x93", // U+0153 latin small ligature oe "\xC2\x9E" => "\xC5\xBE", // U+017E latin small letter z with caron "\xC2\x9F" => "\xC5\xB8", // U+0178 latin capital letter y with diaeresis ); $chr = array_keys ($normalization_map); // but: for efficiency you should $rpl = array_values($normalization_map); // pre-calculate these two arrays $str = str_replace($chr, $rpl, $str);