csv C++ getline添加空格

m2xkgtsf  于 2022-12-06  发布在  其他
关注(0)|答案(4)|浏览(139)

我已经尝试修复这个问题好几天了,但还是无法修复。基本上,我的代码应该读取wmic生成的.csv文件,并将其保存到一个结构体中。我可以读取数据,并将其存储起来。但是数据在每个字符后都有一个额外的空格。我试过切换到Unicode版本的函数并使用宽字符串,但它们只会把数据弄得更乱(他们把“n”变成了“”)。
下面是我认为存在问题的代码:

system("wmic product get name,version,installdate,vendor /format:csv > product.txt");

std::ifstream infoFile("./program.txt"); // The file wmic wrote in csv format.

if(infoFile.is_open())
{
    std::string line;
    int lineNum = 0;

    while(getline(infoFile, line))
    {
        lineNum++;
        std::cout << "\nLine #" << lineNum << ":" << std::endl;

        Program temp;
        std::istringstream lineStream(line);
        std::string cell;
        int counter = 0;
        int cellNum = 0;

        while(getline(linestream, cell, ','))
        {
            cellNum++;
            std::cout << "\nCell #" << cellNum << ":" << cell << std::endl;

            switch(counter)
            {
            case 0:
                break;
            case 1:
                temp.installDate = cell;
                break;
            case 2:
                temp.name = cell;
                break;
            case 3:
                temp.vendor = cell;
                break;
            case 4:
                temp.version = cell;
                break;
            default:
                std::cout << "GetProductInfo(): Invalid switch value: " << counter << std::endl;
                break;
            }
            counter++;
        }

        information->push_back(temp); // Vector to save all of the programs.
    }

    infoFile.close();
}
else
{
    std::cout << "GetProductInfo(): Failed to open the input file." << std::endl;
    return 1;
}

return 0;
}

**编辑:**好的,我正在尝试写BOM(FF FE 0 D 00 0A),因为它以前没有被写过。我正在写一个带有十六进制值的字符数组,但是有一个额外的0x 0 D被添加(FF FE 0 D 00 0 D 0A)。它也保存了带有额外空间的内部变量。这 * 可能 * 不是一个问题,因为我可以修改我的代码来解决它,但是这不是最优的。有什么想法吗?
**Edit 2:**所以我想我不需要BOM。我现在的主要问题是阅读UTF-16 LE文件,并将数据保存到一个没有多余空格的结构体中。我需要一些帮助,以 * 正确 * 的方式来做这件事,因为我想找出如何在未来防止这种情况。感谢大家的帮助,这个bug很严重。

bvpmtnay

bvpmtnay1#

这听起来很像是文本编码问题,所以我继续尝试运行您提供的命令,果然,输出文件是用UCS 16 LE编码的。(这是16位字符,little-endian。)尝试在十六进制编辑器中打开文件,看看它实际上是什么样子的。
在尝试使用宽字符串时,您的方法是正确的,但处理Unicode可能会很棘手。接下来的几段将为您提供一些如何处理这种困难的方法的提示,但如果您需要一个快速而简单的解决方案,请跳到最后。
有两件事需要注意。首先,确保你也使用了宽流,比如wcout。值得将每个字符转换为int,以仔细检查输出格式是否有问题。
第二,wcout,wstring等的格式不是标准的。在一些编译器上,每个字符2个字节,而在另一些编译器上,每个字符4个字节。你通常可以在编译器设置中更改这一点。C11还提供了std::u16 string和std::u32 string,它们对它们的大小有更明确的规定。
不幸的是,使用C
库阅读Unicode文本可能会相当麻烦,因为即使您有合适的字符串大小,您也需要处理BOM和endian格式,更不用说规范化了。
有一些库可以帮助实现这一点,但最简单的解决方案可能只是在记事本中打开txt文件,选择“保存为”,然后选择一种您更喜欢的编码,如ANSI。

**编辑:**如果您不满意这种快速而肮脏的解决方案,并且不想使用更好的Unicode库,则可以使用标准库,但前提是您使用的编译器支持C++11,例如Visual Studio 2012。

C++11增加了一些codecvt facet来处理不同Unicode文件类型之间的转换。这应该能满足你的需要,但是库的这一部分的底层设计是在过去设计的,可能很难理解。抓紧你的裤子。
在打开ifstream的行下面,添加以下代码:

infoFile.imbue(std::locale(infoFile.getloc(), new std::codecvt_utf16<char, 0x10FFFF, std::consume_header>));

我知道这看起来有点吓人。它所做的是从现有的语言环境的副本中创建一个“语言环境”,然后向该语言环境添加一个“方面”来处理格式转换。
“语言环境”处理一大堆东西,大多数与本地化有关(例如如何标点货币,例如“100.00”与“100,00”)。语言环境中的每个规则都被称为一个方面。在C标准库中,文件编码被视为这些方面之一。
(背景:回想起来,将文件编码与本地化混合起来可能不是一个非常明智的想法,但在设计库的这一部分时,文件编码通常由程序的语言决定,因此我们就陷入了这种情况。)
因此,上面的locale构造函数将文件流创建的默认locale的副本作为其第一个参数,第二个参数是要使用的新方面。
codecvt_utf16是一个用于与utf-16进行转换的方面。第一个参数是“wide”类型,也就是说,程序使用的类型,而不是字节流中使用的类型。我在这里指定了char,它可以在Visual Studio中使用,但根据标准,它实际上是无效的。我将在后面介绍。
第二个参数是您希望在不引发错误的情况下接受的最大Unicode值,在可预见的将来,0x 10 FFFF表示最大的Unicode字符。
最后一个参数是一个位掩码,它可以改变facet的行为。我认为std::consume_header对您特别有用,因为wmic会输出一个BOM(至少在我的机器上是这样)。这将使用该BOM,并根据它得到的内容选择是将其视为小端还是大端流。
你也会注意到我在堆栈上用new创建了facet,但是我没有在任何地方调用delete。这不是一个在现代C
中设计库的非常安全的方法,但是就像我说的,locale是库中相当古老的部分。
请放心,您不需要delete这个方面。这并没有很好地记录下来(因为实际上很少使用locale),但是默认构造的方面会自动地被它所附加的locale delete d。
现在,还记得我说过使用char作为wide类型是无效的吗?标准要求您必须使用whcar_tchar16_tchar32_t,如果您希望支持非ASCII字符,您肯定会希望这样做。使其有效的最简单方法是使用wchar_t,更改ifstreamstringcoutistringstreamwifstreamwstringwcoutwistringstream,然后确保字符串/字符常量前面有一个L,如下所示:

std::wcout << L"\nLine #" << lineNum << L":" << line << std::endl;

这些就是使用宽字符串所需的所有更改。但是,还要注意Windows控制台不能处理非ANSI字符,因此,如果您试图输出这样的字符(当我运行代码时遇到了一个™字符),wcout流将无效并停止输出任何内容。如果您要输出到文件,这应该不是问题。
你可能知道我对标准库的这一部分并不特别感兴趣。实际上,大多数想使用Unicode的人会使用不同的库(就像我在评论中提到的那些),或者使用他们自己的编码器/解码器。

q3qa4bjr

q3qa4bjr2#

如果您的数据没有任何您需要的空格,您可以使用我的例子:

std::string s = "test, delim, ";
std::string delims = ", ";

size_t pos = 0;
std::string token;

while((pos=s.find(delimiter))!=std::string::npos)) 
{ token = s.substr(0,pos);
  std::cout<<token<<std::endl;
  s.erase(0, pos + delimiter.length());
}
std::cout<<s<<std::endl //last word

或者,您可以使用cstring库中的strtok。您也可以检查我的问题,它是非常相同的:strtok() analogue in C++

8gsdolmq

8gsdolmq3#

如果数据在每个字符后都有一个额外的空格,我想这意味着它在 * 一个常规空格 * 后也有一个额外的空格。
这样你就可以安全地擦除前面没有空格的每个空格(实际上是每个字符)。这假设你在原始数据中的一行中没有两白色,但如果你有,你只需要一个额外的标志来处理。
所以你的代码可能会变成这样:

while(getline(infoFile, line))
{
    int lsize = line.size(), at = 1;
    for(int i = 1; i < lsize; ++i)
        if(line[i-1] == ' ') line[at++] = line[i];
        // if there is no space behind it, skip it, it is a broken space itself!
    line.resize(at);

    lineNum++;
    // std::cout << "\nLine #"...

我意识到这并不完全理想,因为您实际上并没有阻止核心问题的发生,但考虑到您已经尝试了几天,这至少通过在问题发生后修复它来有效地减轻问题。
检查live demo

xbp102n0

xbp102n04#

在我的例子中,我通过使用Notepad++将编码更改为utf8来解决这个问题。
1.从“编码”菜单:

1.单击utf8进行更改,然后保存:

相关问题