使用PHP转换所有类型的智能引号

使用PHP转换所有类型的智能引号,php,html,unicode,replace,double-quotes,Php,Html,Unicode,Replace,Double Quotes,在处理文本时,我尝试将所有类型的智能引号转换为常规引号。然而,我编译的以下函数似乎仍然缺乏支持和适当的设计 有人知道如何正确地转换所有的文件吗 function convert_smart_quotes($string) { $quotes = array( "\xC2\xAB" => '"', // « (U+00AB) in UTF-8 "\xC2\xBB" => '"', // » (U+00BB) in UTF-8

在处理文本时,我尝试将所有类型的智能引号转换为常规引号。然而,我编译的以下函数似乎仍然缺乏支持和适当的设计

有人知道如何正确地转换所有的文件吗

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;
}

注意:这个问题是一个完整的查询,涉及到各种各样的引用,包括这是一个“重复”的问题,正如询问所有轮胎尺寸是询问汽车轮胎尺寸的“重复”一样。

您需要类似的内容(假设UTF-8输入,忽略CJK(中文、日文、韩文)):

背景如下:

每个Unicode字符只属于一个,其中可以包含引号字符的字符如下:

  • Ps
  • Pe
  • Pi
  • Pf
  • Po
(这些页面可以方便地检查您是否遗漏了任何内容-还有一个)

有时,在启用Unicode的正则表达式中输入字符串是很有用的

此外,Unicode字符有,您感兴趣的是。不幸的是,这些在正则表达式中是不可访问的

在维基百科你可以找到。最后一个参考是unicode.org,但这是一个ASCII文本文件

如果您也需要翻译CJK字符,您只需获得它们的代码点,确定它们的翻译,并找到它们的UTF-8编码,例如,通过在fileformat.info中查找(例如,对于U+301E:)

关于Windows代码页1252:定义前256个代码点以表示与完全相同的字符,但ISO-8859-1经常被混淆,因此所有浏览器都呈现范围0x80-0x9F,该范围在ISO-8859-1中为“空”(更确切地说:它包含控制字符),就好像它是Windows代码页1252一样。列出Unicode等效项

注意:通常比。使用您的输入和PHP版本计时。如果速度足够快,您可以直接使用类似my
$chr\u 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)。这个正则表达式可以稍加增强,但不幸的是,它可以证明,对于编码检测问题,没有完全万无一失的解决方案


如果要将源自Windows代码页1252的0x80-0x9F范围规范化为常规Unicode代码点,可以这样做(并删除上面的
$chr_映射的第一部分):


您可以使用此函数转换所有字符:

$output = iconv('UTF-8', 'ASCII//TRANSLIT', $input);
确保并根据需要更改您的类型


(注意:这是另一个类似的问题)。

您替换智能报价的目的是什么?通常最好保存它们;如果您在处理字符时遇到问题,那么很可能您在处理所有其他非ASCII字符时也遇到问题,这些问题不会通过隐藏智能引号而消失。这段代码试图将文本处理为UTF-8和ISO-8859-1,同时处理原始文本和HTML,这是一个混乱的过程,通常会严重损坏许多其他Unicode字符,而不仅仅是引号。@bobince,我正在进行字符串解析,引号字符对我来说很重要。我确实按原样处理了其余的unicode标志符号。@bobince我很乐意给出一个同样处理其他字符的答案-但我关心的是识别所有引号标志符号,这样我就可以分析字符串,而不用担心其他几十种形式。你想做什么样的分析,需要将不同类型的报价转换为一个报价吗?将例如
'don'
转换为使用所有撇号似乎会使解析变得更加困难。是的,如果您的输入肯定是HTML格式的文本内容,这将很好。有一个细微的区别:在非基于XML的HTML中,字符引用的范围为
和#128
和#159
€;
ÿ;
)由web浏览器解码为具有相同编号的Windows代码页1252代码单元的字符,而不是您预期的字符U+0080到U+00FF。PHP不会重现这种历史怪癖,并将这些序列中的AppSand序列放在字符串中。@ SEBASTI NGRigOLI,你可以在这里读到:正如它所说的:“你可以考虑<代码> \x/Code >点的Unicode版本。”更准确地说,它匹配UTF-8非修饰字符(可选地后跟修饰字符),从开始(
^
)到结束(
$
)。我不知道它是否也会检查它们修改的字符的修饰符的有效性,但它肯定会检查整个字符串是否由有效的UTF-8字节序列组成(对有效的Unicode代码点进行编码),并且它不是以修饰符开头的。@SebastiánGrignoli,对不起,我应该说“组合标记”(
\p{M}
)不是“修饰语”@WalterTross-非常感谢-我正在寻找一些现成的解决方案,但找不到。相反,我创建了一个用于此目的的包-使用上述部分-希望您不介意。这个问题在网上唯一完整正确的答案(可能不是真的,但你知道我的意思)。太糟糕了,它在相关搜索中排名不高。@FrankForte一般来说是正确的,但如果你仔细阅读,我写了“在其他任何事情之前”来澄清,这不仅仅是聪明的引用,所以可能会产生意想不到的后果。你救了我的命,非常感谢你!!
$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);
$output = iconv('UTF-8', 'ASCII//TRANSLIT', $input);