2011-12-03 36 views
4

我刚开始涉足php,恐怕我需要一些帮助来弄清楚如何操纵utf-8字符串。Php无法找到拆分utf-8字符串的方法

我正在ubuntu 11.10 x86,php版本5.3.6-13ubuntu3.2中工作。我有一个UTF-8编码的文件(VIM :set encoding证实了这一点),然后我用mb_detect_encoding($line)报告UTF-8

  • 使用

    $file = fopen("file.txt", "r"); 
    while(!feof($file)){ 
        $line = fgets($file); 
        //... 
    } 
    fclose($file); 
    
    • 继续读它,如果我做echo $line我可以看到正确的线(无在浏览器
      • 所以我想一切都很好,浏览器和Apache。虽然我没有搜索我的Apache配置为AddDefaultCharset,并试图将HTTP元标记的字符编码(以防万一)

    当我尝试拆分使用$arr = mb_split(';',$line)字符串结果数组字段包含错位utf-8字符(mb_detect_encoding($arr[0])也报告utf-8)。

    所以echo $arr[0]会导致类似这样的事情:ΑΘΗÎÎ

    我试过设置mb_detect_order('utf-8'),mb_internal_encoding('utf-8'),但没有任何改变。我还尝试使用this w3 perl regex手动检测utf-8,因为我在某处读取mb_detect_encoding有时可能失败(神话?),但结果也一样。

    所以我的问题是我该如何正确拆分字符串?正在走下mb_路径错误的路?我错过了什么?

    谢谢你的帮助!

    更新:我加入样品字符串和BASE64当量(感谢@克里斯他建议)

    1. original string: "ΑΘΗΝΑ;ΑΙΓΑΛΕΩ;12242;37.99452;23.6889" 
    2. base64 encoded: "zpHOmM6Xzp3OkTvOkc6ZzpPOkc6bzpXOqTsxMjI0MjszNy45OTQ1MjsyMy42ODg5" 
    3. first part (the equivalent of "ΑΘΗΝΑ") base64 encoded before splitting: "zpHOmM6Xzp3OkQ==" 
    4. first part ($arr[0] after splitting): "ΑΘΗÎΑ" 
    5. first part after splitting base64 encoded: "77u/zpHOmM6Xzp3OkQ==" 
    

    好了,这样做后,似乎有3之间的77u/差异。和5.哪个according to this是utf-8 BOM标记。那我该如何避免呢?

    更新2:我醒来后,今天精神焕发,心中有提示,我再次尝试。看起来$line=fgets($file)正确读取第一行(没有损坏的字符),并且每个后续行都失败。那么我base64_encoded的第一行和第二行,以及77u/ bom出现在base64'd字符串的第一行只有。然后我在vim中打开了违规文件,并输入:set nobomb:w来保存没有bom的文件。再次启动PHP表明第一行也被破坏了。根据@ hakre的remove_utf8_bom我增加它的功能互补

    function add_utf8_bom($str){ 
        $bom= "\xEF\xBB\xBF"; 
        return substr($str,0,3)===$bom?$str:$bom.$str; 
    } 
    

    每一行正确读取。

    我不太喜欢这个解决方案,因为它看起来非常非常黑客(我不相信整个框架/语言没有提供处理nobombed字符串的方法)。那么你知道另一种方法吗?否则,我将继续上述。

    感谢@chris,@hakre和@jacob的时间!

    更新3(液):原来,毕竟,这是一个浏览器的东西:它是不够添加header('Content-type: text/html; charset=UTF-8')和meta标签,如<meta http-equiv="Content-type" value="text/html; charset=UTF-8" />。它也必须正确地包含在<html><body>部分,否则浏览器无法正确理解编码。感谢@jake提供的建议。

    故事的士气:在尝试编码浏览器之前,我应该学习更多关于html的知识。感谢大家的帮助和耐心。

  • +1

    我建议你发布样本字符串(拆分之前和之后)以供检查的人员使用。为了保护他们二进制安全,base64_encode()他们,否则细节不会通过网页浏览器和stackoverflow等保存... – goat

    +0

    @chris +1看来,与base64你可能会到东西 – bottlenecked

    +0

    东西是真的这里奇怪。我总是在PHP中使用没有BOM的UTF8字符串,它没有任何问题。你如何输出变量?你只是'echo $ line'?您输出的是整个网页,即文档类型,标题等?或者你是否在命令行上使用PHP? –

    回答

    1

    当你用PHP编写调试/测试脚本,请确保您的输出或多或少有效的HTML页面上完美UTF-8编码字符串,只要工作。

    我喜欢用类似下面的PHP文件:

    <!DOCTYPE html> 
    <html> 
        <head> 
        <meta charset=utf-8> 
        <title>Test page for project XY</title> 
        </head> 
        <body> 
        <h1>Test Page</h1> 
        <pre><?php 
         echo print_r($_GET,1); 
        ?></pre> 
        </body> 
    </html> 
    

    如果不包含任何HTML标签,浏览器可能解释文件为文本文件和各种奇怪的事情会发生。在你的情况下,我假设浏览器将该文件解释为Latin1编码的文本文件。我假定它与BOM一起工作,因为每当BOM出现时,浏览器都会将该文件识别为UTF-8文件。

    +0

    猜对了!我现在更聪明了:P – bottlenecked

    1

    mb_splitDocs功能应该是好的,但你应该定义它的使用,以及与mb_regex_encodingDocs字符集:

    mb_regex_encoding('UTF-8'); 
    

    关于mb_detect_encodingDocs:它可能会失败,但通过这样的事实,你永远无法检测只是编码。你要么知道它,要么你可以尝试,但这就是全部。编码检测主要是赌博游戏,但是您可以使用该函数的严格参数并指定要查找的编码。

    如何删除BOM面膜:

    您可以过滤字符串输入和删除UTF-8 BOM的小助手功能:

    /** 
    * remove UTF-8 BOM if string has it at the beginning 
    * 
    * @param string $str 
    * @return string 
    */ 
    function remove_utf8_bom($str) 
    { 
        if ($bytes = substr($str, 0, 3) && $bytes === "\xEF\xBB\xBF") 
        { 
         $str = substr($str, 3); 
        } 
        return $str; 
    } 
    

    用法:

    $line = remove_utf8_bom($line); 
    

    有可能有更好的方法来做到这一点,但这应该起作用。

    +0

    谢谢,但mb_regex_encoding似乎没有工作 – bottlenecked

    +0

    我没有问题与你的字符串,实际上即使是一个简单的爆炸应该与UTF-8编码的字符串一起工作。请参阅http://codepad.viper-7.com/eODqA5 - 看起来您将结果视为ISO-8859- *。 – hakre

    +0

    使用add_utf8_bom,explode按预期为每行工作。如果一个更好的(也就是不那么骇人)的解决方案不会出现,我会接受这个答案 – bottlenecked

    1

    编辑,我刚刚阅读您的帖子。您建议这应该输出错误,因为您建议BOM是由mb_split()引入的。

    header('content-type: text/plain;charset=utf-8'); 
    $s = "zpHOmM6Xzp3OkTvOkc6ZzpPOkc6bzpXOqTsxMjI0MjszNy45OTQ1MjsyMy42ODg5"; 
    $str = base64_decode($s); 
    
    $peices = mb_split(';', $str); 
    
    var_dump(substr($str, 0, 10) === $peices[0]); 
    var_dump($peices); 
    

    是吗?它符合我的预期(布尔真实,并在阵列中的字符串是正确的)

    +0

    是的,它正如你所说的那样工作。从文件本身读取相同的行时,问题似乎出现 – bottlenecked

    +0

    您确定在发布base64_encoded字符串时没有混淆?因为原始base64字符串没有BOM,并且我假设它应该是直接从fgets返回的值,也是第一行。 – goat

    +0

    是的。疯玩。这是一个“从编辑器手动复制的行,然后粘贴到php文件作为base64_encode的参数”类的东西,因为我目前没有明白这一点的全部含义。对不起,红色鲱鱼:( – bottlenecked

    4

    UTF-8具有非常好的功能,它是ASCII兼容的。有了这个,我的意思是:当编码成UTF-8

  • 没有其他字符将被编码为ASCII字符
  • 这意味着,当您尝试拆分

    • ASCII字符保持不变UTF-8字符串由分号字符;,这是一个ASCII字符,您可以使用标准的单字节字符串函数。

      在你的例子中,你可以使用explode(';',$utf8encodedText),一切都应该按预期工作。

      PS:由于UTF-8编码为prefix-free,因此您可以使用任何UTF-8编码分隔符实际使用explode()

      PPS:它似乎是你试图解析一个CSV文件。看看fgetcsv()函数。它应使用ASCII字符分隔符,报价等

    +0

    的确,爆炸是我最初使用的,当我没有得到它的工作,它后来引导我阅读有关mbstrings – bottlenecked

    +0

    然后你的问题可能是HTML页面的输出编码不是UTF-8。检查你是否在页眉的某处有'! –

    +0

    我试过了(它在过长的问题陈述中也有提及),但是也是nada。再次发现新的发现。 – bottlenecked