如何处理在某些元素中使用 nul char 的 CSV 行

How to process CSV lines with nul char in some elements?

本文关键字:nul char CSV 元素 何处理 处理      更新时间:2023-10-16

读取和解析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, '');
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
    }

它工作正常...直到出现使用 NUL 字符作为行中的值的文件。问题是,当分配s变量时,nul会切断行的其余部分。在观察到的情况下,它发生在使用 1250 编码的文件上。但它也可能发生在 UTF 编码文件中。

如何解决问题?

NUL 字符问题通过使用 C++ 或 Windows 函数来解决。在这种情况下,最简单的解决方案是MultiByteToWideChar它将接受显式字符串长度,因此它不会停止在 NUL 上。