我已经尝试修复这个问题好几天了,但还是无法修复。基本上,我的代码应该读取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很严重。
4条答案
按热度按时间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
的行下面,添加以下代码:我知道这看起来有点吓人。它所做的是从现有的语言环境的副本中创建一个“语言环境”,然后向该语言环境添加一个“方面”来处理格式转换。
“语言环境”处理一大堆东西,大多数与本地化有关(例如如何标点货币,例如“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),但是默认构造的方面会自动地被它所附加的localedelete
d。现在,还记得我说过使用
char
作为wide类型是无效的吗?标准要求您必须使用whcar_t
、char16_t
或char32_t
,如果您希望支持非ASCII字符,您肯定会希望这样做。使其有效的最简单方法是使用wchar_t
,更改ifstream
,string
、cout
和istringstream
到wifstream
、wstring
、wcout
和wistringstream
,然后确保字符串/字符常量前面有一个L
,如下所示:这些就是使用宽字符串所需的所有更改。但是,还要注意Windows控制台不能处理非ANSI字符,因此,如果您试图输出这样的字符(当我运行代码时遇到了一个™字符),wcout流将无效并停止输出任何内容。如果您要输出到文件,这应该不是问题。
你可能知道我对标准库的这一部分并不特别感兴趣。实际上,大多数想使用Unicode的人会使用不同的库(就像我在评论中提到的那些),或者使用他们自己的编码器/解码器。
q3qa4bjr2#
如果您的数据没有任何您需要的空格,您可以使用我的例子:
或者,您可以使用
cstring
库中的strtok
。您也可以检查我的问题,它是非常相同的:strtok() analogue in C++8gsdolmq3#
如果数据在每个字符后都有一个额外的空格,我想这意味着它在 * 一个常规空格 * 后也有一个额外的空格。
这样你就可以安全地擦除前面没有空格的每个空格(实际上是每个字符)。这假设你在原始数据中的一行中没有两白色,但如果你有,你只需要一个额外的标志来处理。
所以你的代码可能会变成这样:
我意识到这并不完全理想,因为您实际上并没有阻止核心问题的发生,但考虑到您已经尝试了几天,这至少通过在问题发生后修复它来有效地减轻问题。
检查live demo。
xbp102n04#
在我的例子中,我通过使用Notepad++将编码更改为utf8来解决这个问题。
1.从“编码”菜单:
1.单击utf8进行更改,然后保存: