【问题标题】:Unexpected result while counting letters in file计算文件中的字母时出现意外结果
【发布时间】:2019-10-27 05:33:44
【问题描述】:

所以,当我遇到异常行为时,我正在编写一个用于静态密码分析的程序。

首先,我写了字符计数器,这就是问题出现的地方。

我有文件:

//alphabet.txt
abcdefghijklmnopqrstuvwxyz

当我尝试计算字母频率时,得到了一些有趣的结果! (不要关注第一行,它只是说ifstream是打开的)

D:\Workspaces\EclipseWS\StaticAnalysis\src>g++ main.cpp

D:\Workspaces\EclipseWS\StaticAnalysis\src>a.exe
1!
a       1
b       1
c       1
d       1
e       1
f       1
g       1
h       1
i       1
j       1
k       1
l       1
m       1
n       1
o       1
p       1
q       1
r       1
s       1
t       1
u       1
v       1
w       1
x       1
y       1
z       2

如您所见,程序说“z”在文本中出现了 2 次,但实际上没有。

现在,技术细节。

Operating System: Windows 10 Enterprise LTSC
System Type: 64bit
C++ compiler: MinGW
alphabet.txt File encoding: ANSI

代码,我解析了文件:

#include <fstream>
#include <iostream>
#include <map>
#include <string>

using namespace std;

#define encrypted_fname "D:\\Workspaces\\EclipseWS\\StaticAnalysis\\src\\alphabet.txt"

void printMap(const map<char,int>& m){
    for (const auto& p : m){
        cout<<p.first<<'\t'<<p.second<<endl;
    }
}

int main(){
    ifstream ifs(encrypted_fname);
    cout << ifs.is_open() << "!\n";
    map<char,int> letterCount;

    char buffer;

    while(ifs){
        ifs >> buffer;
        letterCount[buffer]+=1;
    }

    printMap(letterCount);

}

我尝试将文件编码更改为 UTF-8

1!
»   1
¿   1
ï   1
a   1
b   1
c   1
d   1
e   1
f   1
g   1
h   1
i   1
j   1
k   1
l   1
m   1
n   1
o   1
p   1
q   1
r   1
s   1
t   1
u   1
v   1
w   1
x   1
y   1
z   2

..Unicode 大端\unicode..

1!
þ   1
ÿ   1
    28
a   1
b   1
c   1
d   1
e   1
f   1
g   1
h   1
i   1
j   1
k   1
l   1
m   1
n   1
o   1
p   1
q   1
r   1
s   1
t   1
u   1
v   1
w   1
x   1
y   1
z   1

但是正如你所看到的,在每种情况下输出都有点蹩脚!

如果您需要,我可以提供更多信息,请告诉我如何获取。

我的主要问题是:为什么会这样?我的代码有错误吗?如何解决?

【问题讨论】:

  • 旁注:char buffer 已签名(对于 127 以上的任何字符为负数)。将其用作数组的索引时要小心。
  • 而且我相信ifstream 将打开您的文件以作为 ascii(每个字符 1 个字节)读取,无论您的文件设置为什么编码。只是如果您更改文件编码,那么一些附加信息将(隐式)添加到该文件中,然后当您使用ifstream 阅读时,这些信息将显示为“奇怪的字符”。
  • 您的问题与以下事实有关,我认为,while(ifs) 在达到EOF 字符时为真,但随后ifs &gt;&gt; buffer 实际上并没有将其加载到buffer 中,这仍然保持其最后一个值(恰好是z)。有关详细信息,请参阅this answer
  • ^^^ iow。将您的 while-condition 更改为 while (ifs &gt;&gt; buffer) 并从循环本身中删除 ifs &gt;&gt; buffer
  • @goodvibration "char buffer is signed" - 这取决于编译器的实现。 char 可以签名或不签名。

标签: c++ unicode encoding utf-8 ansi


【解决方案1】:
 while (1) {
        ifs >> buffer;
        if (! ifs) break;
        letterCount[buffer]+=1;
    } ;

应该这样做。如果上次读取成功,“ifs”不会为假, 只有当试图阅读超出其末尾时,它才会被评估为 “错误的”。但在那时,“z”是最后一个字母不是 被任何东西覆盖,并被计算两次。

这种读取文件的语法是对它的时间的尝试 被创建为具有用于文本阅读的“漂亮界面”。 我会说它不能比使用文件调用 read(ifs, &amp;buffer, 1) 更好。

您的其他奇怪结果是由于您总是从文件中读取字节,并且 从不尝试将这些字节解码为文本 - 因此 BOM 标记和 您称为“unicode big endian”(正确名称为“utf-16 big endian”)的 2 字节编码的额外字节将其所有字节视为字符。

【讨论】:

    猜你喜欢
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-25
    • 2014-12-01
    • 2011-10-26
    • 1970-01-01
    相关资源
    最近更新 更多