std::ifstream gcount 文档中的“提取的字符数”是什么意思?

What is meant by "number of characters extracted" in the std::ifstream gcount doc?

提问人:Hyena 提问时间:1/14/2023 最后编辑:Hyena 更新时间:1/15/2023 访问量:85

问:

该方法的文档中说:ifstream::getline

此函数成功读取和存储的字符数可以通过调用成员 gcount 来访问。https://cplusplus.com/reference/istream/istream/getline/

在任何情况下,如果计数> 0,它将空字符 CharT() 存储到数组的下一个连续位置并更新 gcount()。https://en.cppreference.com/w/cpp/io/basic_istream/getline

从上述两个资源中可以推断出,即使在遇到文件末尾 (EOF) 后,也应该更改 gcount。这是因为任何案例都包括 EOF 案例,并且我们都知道更新只有在更改目标记录时才是更新。ifstream::getline

在方法的文档中说:ifstream::gcount

返回上次对对象执行的未格式化输入操作所提取的字符数。https://cplusplus.com/reference/istream/istream/gcount/

返回最后一个未格式化输入操作提取的字符数,如果该数字不可表示,则返回 std::streamsize 的最大可表示值。https://en.cppreference.com/w/cpp/io/basic_istream/gcount

如果是从 ifstream 中提取的字符数,那么 getline 的 CPlusPlus.com 文档一定是错误的,因为它指出“字符已成功读取和存储”。

此外,CppReference.com 是错误的,因为它指出“无论如何......更新 gcount()“,但当在行结束分隔符之前遇到 EOF 时,不会更新 gcount

如果是写入 的数组缓冲区参数的字符数,则标准库存在错误。在执行期间,如果行过早地以文件结束 (EOF) 结束,则 null 字符将追加到数组缓冲区的末尾,但 gcount 不会相应地更新ifstream::getlineifstream::getline

下面是举例说明这种困境的代码。

#include <stdlib.h>
#include <iostream>
#include <array>
#include <fstream>
#include <limits>
#include <cstring>

int main(int argc, char **argv) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " file\n";
        return EXIT_FAILURE;
    }

    std::array<char, 10> buf;
    std::ifstream file;
    file.open(argv[1], std::ifstream::in);

    do {
        file.clear();
        file.getline(buf.data(), buf.size());
        std::streamsize gcount = file.gcount();

        if (file.bad() || gcount <= 0) {
            break;
        }

        if (!file.fail()) {
            std::cerr
                << "LINE: [" << buf.data() << "] gcount "
                << std::to_string(gcount) << ", strlen "
                << std::to_string(strlen(buf.data()))
                << (file.eof() ? " (EOF)\n" : "\n");

            continue;
        }

        // Buffer must have got full. Let's skip to the end of line.
        file.clear();
        file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    while (!file.eof() && !file.bad());

    file.close();

    return EXIT_SUCCESS;
}

这是我得到的文本文件的输出,该文件的最后一行末尾没有换行符

LINE: [dgsagdsa] gcount 9, strlen 8
LINE: [test] gcount 5, strlen 4
LINE: [test123] gcount 8, strlen 7
LINE: [123test] gcount 8, strlen 7
LINE: [] gcount 1, strlen 0
LINE: [xxxxxxx] gcount 8, strlen 7
LINE: [yy] gcount 2, strlen 2 (EOF)

如您所见,在输出的最后一行,gcountstrlen 之间存在差异。

也就是说,现在让我们回到主要问题。

文档提取的字符数是什么意思?std::ifstream::gcount

这个问题有两个部分。

  1. “角色”是什么意思?
  2. “提取”是什么意思?

在这种情况下,一个字符总是一个字节吗?Unicode 字符可以由多个字节组成。行尾序列也可以由多个字节 (CR+LF) 组成。是否会发生(也许在未来)gcount 增加 1 但提取了多个字节的情况?gcount 会不会增加 1 但数组缓冲区中存储了多个字节?

C++ IFSTREAM eof getline 空字符

评论

0赞 463035818_is_not_an_ai 1/14/2023
文件的内容是什么?
0赞 463035818_is_not_an_ai 1/14/2023
这种“差异”是因为读取直到找到分隔符或命中 EOF。并且分隔符不会写入数组。不是错误。getline
2赞 tkausl 1/14/2023
cplusplus.com是错误的。我建议使用:en.cppreference.com/w/cpp/io/basic_istream/getlinecppreference.com
1赞 463035818_is_not_an_ai 1/14/2023
gcount对分隔符进行计数,但该分隔符未写入数组。 不计算在内,因为不是从流中提取的字符。两者都不会添加到阵列中gcountEOFEOF
1赞 463035818_is_not_an_ai 1/14/2023
因为我不明白你的意思。文档在哪里有缺陷?

答:

1赞 Dean Johnson 1/14/2023 #1

让我们以示例中的最后一行为例,然后逐步完成它 - 。yy<eof>

initial state: gcount = 0, strlen(inProgressBuf) == 0
yy<eof>

gcount = 1, strlen(inProgressBuf) == 1
yy<eof>
^

gcount = 2, strlen(inProgressBuf) == 2
yy<eof>
 ^

oh, hit EOF
yy<eof>
  ^

在点击EOF时,已经提取了两个字符,2也是如此。 现在要在缓冲区中附加一个 null 字符 - 这与 无关。实际上只提取了两个字符。gcountgetlinegcount

对于带有分隔符的字符串,假设:yy<lf><eof>

initial state: gcount = 0, strlen(inProgressBuf) == 0
yy<lf><eof>

gcount = 1, strlen(inProgressBuf) == 1
yy<lf><eof>
^

gcount = 2, strlen(inProgressBuf) == 2
yy<lf><eof>
 ^
gcount = 3, strlen(inProgressBuf) == 2
yy<lf><eof>
  ^

当 LF 被命中时,正在从输入中提取一个字符,因此会递增。但是,提取的字符与分隔符匹配,因此不会将其添加到缓冲区中。只需为字符串的 null 终止添加 null 字符即可。gcountgetline

EOF不是一个可以提取的字符,因此达到它不会递增。gcount

我能看到的唯一可能有争议的措辞是 https://en.cppreference.com/w/cpp/io/basic_istream/getline 的摘录:cppreference

在任何情况下,如果计数> 0,它将空字符 CharT() 存储到数组的下一个连续位置并更新 gcount()。

您可以将其解释为附加 null 字符是更新的原因。但是,我相信预期的含义是正在更新,因为.gcountgcountcount > 0

关于如何确定写入的字节数的问题,评论中的建议似乎是合适的:

除非你击中 eof,否则它是gcountgcount + 1

评论

0赞 Hyena 1/15/2023
那么,我们是否可以正式得出结论,没有指定的方法来获取写入数组缓冲区的确切字节数?任何说明格式错误的文件。毕竟,行分隔符理论上不限于 1 个字符,并且可能更多(例如回车符 + 换行符),因此建议是错误的信息。gcount + 1
0赞 Hyena 1/15/2023
从理论上讲,是否可以提取一个字符并将两个字节写入数组缓冲区?如果该字符是 2 字节的 unicode 序列,则它增加了 1,但实际上存储了 2 个字节?因此,从技术上讲,从中获取存储的字节数是错误的,应该在文档的任何地方说明。gcountgcount
0赞 Hyena 1/15/2023
我对原来的问题进行了相当多的编辑。我删除了询问如何获取写入数组缓冲区的字节数的部分,因为我打算为此创建一个新问题。请参阅我问题的结尾,并尝试针对它。在这种情况下,提取是什么意思,字符是什么意思?
1赞 tkausl 1/15/2023
Could it ever theoretically be that one character is extracted and two bytes are written into the array buffer?是的,如果您的字符类型是 2 字节类型。CharT
0赞 Dean Johnson 1/16/2023
我完全同意任何说明写入确切字节数的文档都是错误的。鉴于这是来自 cpluscplus.com,我并不感到惊讶。不过,我不同意建议是坏的。不能将多字符分隔符传递给 ,因此存在多字符行尾这一事实无关紧要。我在 cppreference 上看到的任何地方都表明 gcount 是 的数量,而不是字节。提取是从输入中读取字符。 是 的实例。gcountgcount + 1std::getlinecharacterscharacterCharT