如何处理某些元素中带有 nul char 的 CSV 行?
How to process CSV lines with nul char in some elements?
在读取和解析 CSV 文件行时,我需要处理作为某些行字段的值出现的 nul 字符。有时 CSV 文件采用 windows-1250 编码,有时采用 UTF-8,有时采用 UTF-16,这使情况变得复杂。因此,我已经开始了一些方式,后来发现了 nul char 问题 -- 见下文。
详细信息:我需要将来自第三方的 CSV 文件清理为我们的数据提取器通用的格式(即该实用程序用作过滤器 - 存储一个 CSV 格式到另一个 CSV 表格)。
我最初的做法是以二进制方式打开CSV文件,检查第一个字节是否构成BOM。我知道所有给定的 Unicode 文件都以 BOM 开头。如果没有 BOM,我知道它是 windows-1250 编码。
转换后的 CSV 文件应使用 windows-1250 编码。因此,在检查输入文件后,我使用相关模式打开它,如下所示:
// Open the file in binary mode first to see whether BOM is there or not.
FILE * fh{ nullptr };
errno_t err = fopen_s(&fh, fnameIn.string().c_str(), "rb"); // const fs::path & fnameIn
assert(err == 0);
vector<char> buf(4, '[=10=]');
fread(&buf[0], 1, 3, fh);
::fclose(fh);
// Set the isUnicode flag and open the file according to that.
string mode{ "r" }; // init
bool isUnicode = false; // pessimistic init
if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) // UTF-8 BOM
{
mode += ", ccs=UTF-8";
isUnicode = true;
}
else if ((buf[0] == 0xFE && buf[1] == 0xFF) // UTF-16 BE BOM
|| (buf[0] == 0xFF && buf[1] == 0xFE)) // UTF-16 LE BOM
{
mode += ", ccs=UNICODE";
isUnicode = true;
}
// Open in the suitable mode.
err = fopen_s(&fh, fnameIn.string().c_str(), mode.c_str());
assert(err == 0);
成功打开后,将通过 fgets
或 fgetws
读取输入行——取决于是否检测到 Unicode。然后的想法是,如果较早检测到 unicode,则将缓冲区内容从 Unicode 转换为 1250,或者让缓冲区为 1250。s
变量应包含 windows-1250 编码中的字符串。 ATL::CW2A(buf, 1250)
用于需要转换的时候:
const int bufsize = 4096;
wchar_t buf[bufsize];
// Read the line from the input according to the isUnicode flag.
while (isUnicode ? (fgetws(buf, bufsize, fh) != NULL)
: (fgets(reinterpret_cast<char*>(buf), bufsize, fh) != NULL))
{
// If the input is in Unicode, convert the buffer content
// to the string in cp1250. Otherwise, do not touch it.
string s;
if (isUnicode) s = ATL::CW2A(buf, 1250);
else s = reinterpret_cast<char*>(buf);
...
// Now processing the characters of the `s` to form the output file
}
它工作正常...直到出现一个包含空字符的文件作为行中的值。问题是当 s
变量被赋值时,nul
会截断该行的其余部分。在观察到的情况下,它发生在使用 1250 编码的文件中。但它也可能发生在 UTF 编码的文件中。
如何解决问题?
NUL 字符问题已通过使用 C++ 或 Windows 函数解决。在这种情况下,最简单的解决方案是 MultiByteToWideChar
,它将接受一个明确的字符串长度,正是这样它才不会在 NUL 上停止。
在读取和解析 CSV 文件行时,我需要处理作为某些行字段的值出现的 nul 字符。有时 CSV 文件采用 windows-1250 编码,有时采用 UTF-8,有时采用 UTF-16,这使情况变得复杂。因此,我已经开始了一些方式,后来发现了 nul char 问题 -- 见下文。
详细信息:我需要将来自第三方的 CSV 文件清理为我们的数据提取器通用的格式(即该实用程序用作过滤器 - 存储一个 CSV 格式到另一个 CSV 表格)。
我最初的做法是以二进制方式打开CSV文件,检查第一个字节是否构成BOM。我知道所有给定的 Unicode 文件都以 BOM 开头。如果没有 BOM,我知道它是 windows-1250 编码。 转换后的 CSV 文件应使用 windows-1250 编码。因此,在检查输入文件后,我使用相关模式打开它,如下所示:
// Open the file in binary mode first to see whether BOM is there or not.
FILE * fh{ nullptr };
errno_t err = fopen_s(&fh, fnameIn.string().c_str(), "rb"); // const fs::path & fnameIn
assert(err == 0);
vector<char> buf(4, '[=10=]');
fread(&buf[0], 1, 3, fh);
::fclose(fh);
// Set the isUnicode flag and open the file according to that.
string mode{ "r" }; // init
bool isUnicode = false; // pessimistic init
if (buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) // UTF-8 BOM
{
mode += ", ccs=UTF-8";
isUnicode = true;
}
else if ((buf[0] == 0xFE && buf[1] == 0xFF) // UTF-16 BE BOM
|| (buf[0] == 0xFF && buf[1] == 0xFE)) // UTF-16 LE BOM
{
mode += ", ccs=UNICODE";
isUnicode = true;
}
// Open in the suitable mode.
err = fopen_s(&fh, fnameIn.string().c_str(), mode.c_str());
assert(err == 0);
成功打开后,将通过 fgets
或 fgetws
读取输入行——取决于是否检测到 Unicode。然后的想法是,如果较早检测到 unicode,则将缓冲区内容从 Unicode 转换为 1250,或者让缓冲区为 1250。s
变量应包含 windows-1250 编码中的字符串。 ATL::CW2A(buf, 1250)
用于需要转换的时候:
const int bufsize = 4096;
wchar_t buf[bufsize];
// Read the line from the input according to the isUnicode flag.
while (isUnicode ? (fgetws(buf, bufsize, fh) != NULL)
: (fgets(reinterpret_cast<char*>(buf), bufsize, fh) != NULL))
{
// If the input is in Unicode, convert the buffer content
// to the string in cp1250. Otherwise, do not touch it.
string s;
if (isUnicode) s = ATL::CW2A(buf, 1250);
else s = reinterpret_cast<char*>(buf);
...
// Now processing the characters of the `s` to form the output file
}
它工作正常...直到出现一个包含空字符的文件作为行中的值。问题是当 s
变量被赋值时,nul
会截断该行的其余部分。在观察到的情况下,它发生在使用 1250 编码的文件中。但它也可能发生在 UTF 编码的文件中。
如何解决问题?
NUL 字符问题已通过使用 C++ 或 Windows 函数解决。在这种情况下,最简单的解决方案是 MultiByteToWideChar
,它将接受一个明确的字符串长度,正是这样它才不会在 NUL 上停止。