ifstream::read 不读取无符号字符,即使使用 reinterpret_cast

Posted

技术标签:

【中文标题】ifstream::read 不读取无符号字符,即使使用 reinterpret_cast【英文标题】:ifstream::read not reading unsigned char, even with reinterpret_cast 【发布时间】:2016-10-30 23:49:26 【问题描述】:

我正在尝试让我的代码读取 PPM 图像 (P3),但它无法正常工作。这个想法是获取 3 个无符号字符并将它们存储在 RGB 中。但目前它只会导致获取第一个字符而忽略其余字符。

Image Image::readImg(std::string const &filename) 
    std::ifstream ifs;
    ifs.open(filename.c_str(), std::ios::binary);
    Image _in;
    try 
        if (ifs.fail()) 
            throw("Can't open input file");
        
        std::string header;
        int w, h, max;
        ifs >> header;
        if (strcmp(header.c_str(), "P3") != 0) throw("Can't read input file");
        ifs >> w >> h >> max;
        _in.init(w, h);
        ifs.ignore(256, '\n');
        unsigned char pix[3];
        for (int i = 0; i < h; ++i)
            for (int j = 0; j < w; ++j)
                ifs.read(reinterpret_cast<char *>(pix), 3);
                _in.pixels[i][j].R = pix[0];
                _in.pixels[i][j].G = pix[1];
                _in.pixels[i][j].B = pix[2];
            
        
        std::cout << "|" << _in.pixels[0][0].R << " " << _in.pixels[0][0].G << " " << _in.pixels[0][0].B << "|";
        ifs.close();
    
    catch (const char *err) 
        fprintf(stderr, "%s\n", err);
        ifs.close();
    
    return _in;

注意 std::cout 应该在我的场景 |186 0 255| 中输出,但我得到的是 |1 8 6|。


编辑: 文件(original.ppm)在 Notepad++ 中打开时如下所示(UNIX / UTF-8):

P3
1024 768
255
186 0 255 186 0 255 186 0 255 186 0 255 186 0 255 186 0 255 186 1 255 
186 1 254 186 1 254 185 2 254 185 2 254 185 1 254 185 2 253 185 3 253 
185 2 252 185 3 252 185 3 252 185 3 252 185 3 251 184 4 251 184 4 251 
184 4 251 184 4 251 184 5 250 184 5 250 183 5 250 183 6 249 183 6 249 
183 6 249 183 6 248 183 7 249 183 7 249 183 7 248 183 7 247 183 8 247 
182 7 247 182 8 246 183 9 247 183 9 246 183 9 246 182 9 246 181 9 246 
182 9 246 181 10 245 182 10 245 181 10 244 181 10 245 181 11 244 181 11 244
...

结果应该是: _in.pixels[0][0].R = 186 _in.pixels[0][0].G = 0 _in.pixels[0][0].B = 255 并继续收集文件中所有像素的RGB。

【问题讨论】:

operator&gt;&gt; 用于读取空格分隔的文本。除非P3 后跟一个空格,否则在文件中,这是行不通的。 这不是问题,但不要对控制流使用异常。您不需要关闭文件;析构函数会这样做。因此,当您检测到错误时,打印消息并返回。 "请注意 std::cout 应该在我的场景 |186 0 255| 中输出,但我得到的是 |1 8 6|。"我从中推断出您的输入是文本格式的-这意味着组件不是编码为二进制字节,而是以十进制写入的这些字节的文本表示形式。然而,您尝试在 ifs.read(..., 3) 和接下来的 3 个 assgns 中将它们读取为 3 个字节。 P3 PPM 是一种文本格式。你得到了相同数字的 3 位数字,因为在这些位置有 3 个文本字符 186 如何在每个像素循环中准确读取 3 个值并以某种方式组织它以获得 RGB?请注意,这些应该代表字节。 【参考方案1】:

[更新]这应该可以工作:

int pix[3]; // not an unsigned char
...
ifs >> pix[0] >> pix[1] >> pix[2];

代替:

ifs.read(reinterpret_cast<char *>(pix), 3);

就像你对宽度和高度所做的那样?

ifs >> w >> h >> max;

【讨论】:

还是同样的问题。它确定 1 8 和 6 在它们应该是一个有凝聚力的无符号字符时都是单独的字符。 您的_in.pixels[i][j].R 也是字符?如果是这样,请使用 temp int / uint 值来读取。我已经用它更新了答案。 输出只是乱七八糟的符号。 让我猜猜... �� ?您是否在调试器中检查了值是什么?当然,如果 char 值是,例如65 你会在输出中得到 beatufill 'A' 字母,而对于 186 和 255,你会得到一些“乱码”...... 啊哈,它确实有效。在 UTF-8 中,notepad++ 标识正在获取的值并将其存储为 8 位字符。因此 186 在转换时显示为“xBA”或 186。谢谢 :) 现在我只需要弄清楚如何操作它并输出转换后的值。【参考方案2】:

在流中使用 >> 操作默认会跳过空格。

如果您想保留所有字符,请执行(在读取之前):

ifs >> std::noskipws;

您可能还需要以二进制模式打开文件。但我认为没有必要。

如果你想将两个字节准确地读入一个字符串,你可以使用它来代替 getline:

std::string st;
st.resize(2);
ifs.read(&st[0], st.size());

【讨论】:

做了第一位,考虑到文件的布局方式,这不是问题(参见 OP 中的新编辑)。已经处于二进制模式。和 std::string st(2);无法正确编译。 使用 std::string st ; st.resize(2); 字符串调整大小,看起来它只调整字符串中字符的大小,因此通过执行上述操作,它将读取 st[0] = 1 和 st[1] = 8。这将不工作。 这是一个如何读取两个字节的示例(如在标题中)。如果你想读取三个字节,那就把它改成三个。 我已经这样做了,只是没有提及。 st[0] = 1,st[1] = 8 和 st[2] = 6

以上是关于ifstream::read 不读取无符号字符,即使使用 reinterpret_cast的主要内容,如果未能解决你的问题,请参考以下文章

从文件中读取文本到无符号字符数组

如何使用gets()读取无符号字符数组?

如何在c中读取由空格分隔的无符号字符

使用 Qt 读取无符号字符

C ++从文件流中读取无符号字符

C++ 使用 istringstream 将整数读取为无符号字符