php - 字符集转换:PHP 的 iconv 不起作用
问题描述
我们有一些带有 PHP 后端的内部仪表板,用于上传 CSV 文件。最近我们发现一些 CSV 无法解析:fgetcsv
函数返回false
,这是非常讨厌的,因为我们无法确定 CSV 中的实际问题(例如,在哪一行不是经验问题,哪些字符无法消化等。 )
我们将问题缩小到字符集编码:从 Windows 机器生成的 CSV 失败。Linux 的iconv
命令能够为我们修复 CSV
iconv -c --from-code=UTF-8 --to-code=ASCII path/to/uncleaned.csv > path/to/cleaned.csv
虽然它的PHP 等效项不起作用(尝试使用这两个//IGNORE//TRANSLIT
选项)。
$uncleaned_csv_text = file_get_contents($source_data_csv_filename);
$cleaned_csv_text = iconv('UTF-8', 'ASCII/IGNORE//TRANSLIT', $uncleaned_csv_text);
file_put_contents($source_data_csv_filename, $cleaned_csv_text);
..
$headers = fgetcsv($source_data_csv_filename)
虽然我们可以使用 PHP 的exec
函数来运行 shell 命令
- 它不太理想
- 从安全角度来看,我们组织禁止这种做法(
Travis
不让它通过)
有没有其他方法可以实现这种 CSV“清理”?
更新-1
我们探索了其他几个选项,但没有一个对我们有用
regex
基于清洁forceutf8
包裹mb_convert_encoding
(正如讨论所建议的那样)
更新-2
- 在将 CSV 文本
echo
的sha1
摘要置于 PHP 函数之前和之后iconv
,我们发现它iconv
没有做任何更改 - 同样在我的情况下,无论输入查询如何,
mb_check_encoding
在原始 CSV 的文本输出上: , ,true
windows-1252
ascii
utf-8
解决方案
我一直在研究一个处理 csv 文件的插件(在 Wordpress 中)一段时间(5/5 星评级),我一直在使用mb_convert_encoding()
没有问题。我知道我的用户同时使用 Windows 和 Linux。
基本上:(转为UTF-8,来自:Windows-1252)
$cleaned_csv_text = mb_convert_encoding($uncleaned_csv_text, 'UTF-8', 'Windows-1252');
如果您不知道原件的编码(在您的情况下可能更好):
(转UTF-8)
$cleaned_csv_text = mb_convert_encoding($uncleaned_csv_text, 'UTF-8');
更新:
这是一个更完整的答案,我希望你会觉得有用:我已经与etcfile()
一起使用:str_getcsv()
<?php
$file = "csvfiles/pricelist.csv"; //This is Windows-1252 encoded
//Load a csv file into an array $content_arr
$content_arr = array_map(function($v) {
$delimiter = ';';
return str_getcsv($v, $delimiter);},
file( $file ));
//Do encoding row by row
//and include end of line based on the item in the array $content_arr
$csv = array_map(function($v) {
return mb_convert_encoding($v[0], 'UTF8','Windows-1252') . detect_eol($v[0]);},
$content_arr);
//Save modified file in UTF8
file_put_contents('csvfiles/pricelist_modified.csv', $csv);
//Detects the end-of-line character of a string.
//
//function from
//https://stackoverflow.com/questions/11066857/detect-eol-type-using-php/11066858#11066858
function detect_eol( $str )
{
static $eols = array(
"\0x000D000A", // [UNICODE] CR+LF: CR (U+000D) followed by LF (U+000A)
"\0x000A", // [UNICODE] LF: Line Feed, U+000A
"\0x000B", // [UNICODE] VT: Vertical Tab, U+000B
"\0x000C", // [UNICODE] FF: Form Feed, U+000C
"\0x000D", // [UNICODE] CR: Carriage Return, U+000D
"\0x0085", // [UNICODE] NEL: Next Line, U+0085
"\0x2028", // [UNICODE] LS: Line Separator, U+2028
"\0x2029", // [UNICODE] PS: Paragraph Separator, U+2029
"\0x0D0A", // [ASCII] CR+LF: Windows, TOPS-10, RT-11, CP/M, MP/M, DOS, Atari TOS, OS/2, Symbian OS, Palm OS
"\0x0A0D", // [ASCII] LF+CR: BBC Acorn, RISC OS spooled text output.
"\0x0A", // [ASCII] LF: Multics, Unix, Unix-like, BeOS, Amiga, RISC OS
"\0x0D", // [ASCII] CR: Commodore 8-bit, BBC Acorn, TRS-80, Apple II, Mac OS <=v9, OS-9
"\0x1E", // [ASCII] RS: QNX (pre-POSIX)
"\0x15", // [EBCDEIC] NEL: OS/390, OS/400
"\r\n",
"\r",
"\n"
);
$cur_cnt = 0;
$cur_eol = "\r\n"; //default
//Check if eols in array above exists in string
foreach($eols as $eol){
$char_cnt = mb_substr_count($str, $eol);
if($char_cnt > $cur_cnt)
{
$cur_cnt = $char_cnt;
$cur_eol = $eol;
}
}
return $cur_eol;
}
推荐阅读
- asp.net-mvc - MVC中如何处理一个参数的多个值
- sql - 平面文件 --> BizTalk --> Sql Server - 行序列不匹配
- android - 如何在Android中更改时间选择器的钟面和钟针的颜色
- java - 组装对象时JPA的工作原理
- grpc - 如何将 gRPC 服务公开为 Protobuf 和 JSON
- laravel - Oiset\ShopifyApp\Exceptions\MissingShopDomainException
- typescript - 如何在模块扩充中引用 ComponentOptions 的其他泛型参数?
- reactjs - 使用按钮上的道具路由到新组件单击反应
- python - IndexError:索引 14708 超出轴 0 的范围,大小为 295
- java - Java 6 支持 TLS1.2