最近導出文件遇到fwrite導出亂碼,而且中英文都亂碼,很費解。折騰了一番之后終於找到問題所在了,mark下。
UTF-8 BOM 又叫 UTF-8 簽名,其實 UTF-8 的 BOM 對 UFT-8 沒有作用,是為了支援 UTF-16,UTF-32 才加上的 BOM ,BOM 簽名的意思就是告訴弱編輯器(記事本)當前文件采用何種編碼,方便編輯器識別。
PHP 在設計之初,沒有考慮到 BOM 頭的問題,因此很容易因為 BOM 頭引發詭異的問題,比如編碼轉換失敗,樣式錯亂等等問題,而且此問題相當隱蔽,很難確定發生問題的文件(試想在沒有工具的情況下從上萬的工程文件中找到哪個文件帶有 BOM 頭)。
BOM 頭是隱藏字符,非編輯字符,就像普通空文件一樣,當我們寫 <?php 的時候其實之前已經加了 BOM 頭,如下(file.php):
{BOM頭}<?php
.....
當 file.php 被其他文件包含時,由於 BOM 頭在 php 標簽外,會當作輸出內容輸出到瀏覽器,然后引發問題。
少年,珍愛生命,遠離 BOM 。
php fwrite輸出也遇到了這個問題,判斷是否有bom,如果沒有加手動加上bom字符串,如果有直接輸出,問題解決。
代碼如下:
1 function checkBOM($filename) 2 { 3 if (!file_exists($filename)) { 4 return FALSE; 5 } 6 $contents = file_get_contents($filename); 7 $charset[1] = substr($contents, 0, 1); 8 $charset[2] = substr($contents, 1, 1); 9 $charset[3] = substr($contents, 2, 1); 10 if (ord($charset[1]) == 239 && ord($charset[2]) == 187 && ord($charset[3]) == 191) { 11 return TRUE; 12 } 13 return FALSE; 14 } 15 16 $msg = "你好\n"; 17 //如果默認編碼不是utf8,先用函數utf8_encode將所需寫入的數據變成UTF編碼格式。 18 //$msg = utf8_encode($msg); 19 //$msg = iconv('gbk', 'utf-8', $msg); 20 21 $fileName = 'test'; 22 $filePath = './test.txt'; 23 $checkBom = checkBOM($filePath); 24 // 有bom的情況下"\xEF\xBB\xBF"第一次寫入這段字符不可缺少 25 if ($checkBom == FALSE) { 26 $msg = "\xEF\xBB\xBF" . $msg; 27 } 28 $fp = @fopen($filePath, 'a'); 29 @fwrite($fp, $msg); 30 @fclose($fp);
function checkBOM($filename)
{
if (!file_exists($filename)) {
return FALSE;
}
$contents = file_get_contents($filename);
$charset[1] = substr($contents, 0, 1);
$charset[2] = substr($contents, 1, 1);
$charset[3] = substr($contents, 2, 1);
if (ord($charset[1]) == 239 && ord($charset[2]) == 187 && ord($charset[3]) == 191) {
return TRUE;
}
return FALSE;
}
$msg = "你好\n";
//如果默認編碼不是utf8,先用函數utf8_encode將所需寫入的數據變成UTF編碼格式。
//$msg = utf8_encode($msg);
//$msg = iconv('gbk', 'utf-8', $msg);
$fileName = 'test';
$filePath = './test.txt';
$checkBom = checkBOM($filePath);
// 有bom的情況下"\xEF\xBB\xBF"第一次寫入這段字符不可缺少
if ($checkBom == FALSE) {
$msg = "\xEF\xBB\xBF" . $msg;
}
$fp = @fopen($filePath, 'a');
@fwrite($fp, $msg);
@fclose($fp);
不知道有沒有更好的辦法,歡迎交流。