在PHP中使用文件和utf8

时间:2010-09-26 23:36:20

标签: php file-io unicode utf-8

假设我有一个名为foo.txt的文件,编码在utf8中:

aoeu  
qjkx
ñpyf

我希望得到一个数组,其中包含该文件中的所有行(每个索引一行),其中包含字母aoeuñpyf,并且只包含带有这些字母的行。

我编写了以下代码(也编码为utf8):

$allowed_letters=array("a","o","e","u","ñ","p","y","f");

$lines=array();
$f=fopen("foo.txt","r");
while(!feof($f)){
    $line=fgets($f);
    foreach(preg_split("//",$line,-1,PREG_SPLIT_NO_EMPTY) as $letter){
        if(!in_array($letter,$allowed_letters)){
            $line="";
        }
    }
    if($line!=""){
        $lines[]=$line;
    }
}
fclose($f);

然而,在此之后,$lines数组中只有aoeu行 这似乎是因为不知何故,$allowed_letters中的“ñ”与foo.txt中的“ñ”不同。
此外,如果我打印文件的“ñ”,则会出现一个问号,但如果我按照print "ñ";打印它,它就会起作用。
我怎样才能使它发挥作用?

3 个答案:

答案 0 :(得分:10)

如果您运行的是Windows,则操作系统不会以UTF-8保存文件,但在cp1251(或其他...)中,默认情况下您需要以该格式显式保存文件或运行{{1中的每一行在执行检查之前。即:

utf8_encode()

如果您确定该文件是UTF-8编码的,那么您的PHP文件是否也是UTF-8编码的?

如果一切都是UTF-8,那么这就是你所需要的:

$line=utf8_encode(fgets($f));

(为{unicode chars附加foreach(preg_split("//u",$line,-1,PREG_SPLIT_NO_EMPTY) as $letter){ // ... }

但是,让我建议一种更快捷的方式来执行检查:

u

(添加空格字符以允许空格字符,并删除$allowed_letters=array("a","o","e","u","ñ","p","y","f"); $lines=array(); $f=fopen("foo.txt","r"); while(!feof($f)){ $line=fgets($f); $line = str_split(rtrim($line)); if (count(array_intersect($line, $allowed_letters)) == count($line)) { $lines[] = $line; } } fclose($f);

答案 1 :(得分:2)

在UTF-8中,ñ被编码为两个字节。通常在PHP中,所有字符串操作都是基于字节的,因此当您preg_split输入时,它会将第一个字节和第二个字节拆分为单独的数组项。单独的第一个字节和第二个字节本身都不会匹配$allowed_letters中的两个字节,因此它永远不会匹配ñ

正如Yanick所说,解决方案是添加u修饰符。这使得PHP的正则表达式引擎将模式和输入行都视为Unicode字符而不是字节。很幸运,PHP在这里有特殊的Unicode支持;其他地方PHP的Unicode支持非常不稳定。

比分割更简单快捷的方法是将每一行与字符组正则表达式进行比较。同样,这必须是u正则表达式。

if(preg_match('/^[aoeuñpyf]+$/u', $line))
    $lines[]= $line;

答案 2 :(得分:0)

听起来你已经得到了答案,但重要的是要认识到unicode字符可以以多种方式存储。 Unicode规范化*是一个有助于确保比较按预期工作的过程。