使用 << 读取固定数量的字符在 istream 上

发布于 2024-10-16 22:09:54 字数 546 浏览 6 评论 0 原文

我正在尝试一些 C++ 中的文件读取策略,并且遇到了这个。

ifstream ifsw1("c:\\trys\\str3.txt");
char ifsw1w[3];
do {
    ifsw1 >> ifsw1w;
    if (ifsw1.eof())
        break;
    cout << ifsw1w << flush << endl;
} while (1);
ifsw1.close();

文件的内容是

firstfirst firstsecond
secondfirst secondsecond

当我看到输出时,它被打印,因为

 firstfirst
firstsecond
secondfirst

我期望输出如下:

fir
stf
irs
tfi
.....

此外,我看到“secondsecond”尚未打印。我猜想最后一次读取已经遇到了eof,cout可能还没有被执行。但第一个行为是不可理解的。

I was trying out a few file reading strategies in C++ and I came across this.

ifstream ifsw1("c:\\trys\\str3.txt");
char ifsw1w[3];
do {
    ifsw1 >> ifsw1w;
    if (ifsw1.eof())
        break;
    cout << ifsw1w << flush << endl;
} while (1);
ifsw1.close();

The content of the file were

firstfirst firstsecond
secondfirst secondsecond

When I see the output it is printed as

 firstfirst
firstsecond
secondfirst

I expected the output to be something like:

fir
stf
irs
tfi
.....

Moreover I see that "secondsecond" has not been printed. I guess that the last read has met the eof and the cout might not have been executed. But the first behavior is not understandable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

奈何桥上唱咆哮 2024-10-23 22:09:54

提取运算符没有 ifsw1w 变量大小的概念,并且(默认情况下)将提取字符,直到遇到空格、null 或 eof。这些可能存储在 ifsw1w 变量之后的内存位置中,如果定义了其他变量,这将导致严重的错误。

为了获得所需的行为,您应该能够

ifsw1.width(3);

限制要提取的字符数。

The extraction operator has no concept of the size of the ifsw1w variable, and (by default) is going to extract characters until it hits whitespace, null, or eof. These are likely being stored in the memory locations after your ifsw1w variable, which would cause bad bugs if you had additional variables defined.

To get the desired behavior, you should be able to use

ifsw1.width(3);

to limit the number of characters to extract.

东走西顾 2024-10-23 22:09:54
  1. 使用 std::istream& 实际上是不可能的。安全地使用operator>>(std::istream&, char *)——在这方面就像gets——你无法指定缓冲区大小。流只是写入缓冲区,从末尾开始。 (上面的示例调用了未定义的行为)。要么使用接受 std::string 的重载,要么使用 std::getline(std::istream&, std::string)

  2. 检查eof()不正确。您需要 fail() 来代替。您实际上并不关心流是否位于文件末尾,您只关心提取信息是否失败。

对于这样的事情,您可能最好将整个文件读入字符串并从该点开始使用字符串操作。您可以使用字符串流来做到这一点:

#include <string> //For string
#include <sstream> //For stringstream
#include <iostream> //As before

std::ifstream myFile(...);
std::stringstream ss;
ss << myFile.rdbuf(); //Read the file into the stringstream.
std::string fileContents = ss.str(); //Now you have a string, no loops!
  1. It's virtually impossible to use std::istream& operator>>(std::istream&, char *) safely -- it's like gets in this regard -- there's no way for you to specify the buffer size. The stream just writes to your buffer, going off the end. (Your example above invokes undefined behavior). Either use the overloads accepting a std::string, or use std::getline(std::istream&, std::string).

  2. Checking eof() is incorrect. You want fail() instead. You really don't care if the stream is at the end of the file, you care only if you have failed to extract information.

For something like this you're probably better off just reading the whole file into a string and using string operations from that point. You can do that using a stringstream:

#include <string> //For string
#include <sstream> //For stringstream
#include <iostream> //As before

std::ifstream myFile(...);
std::stringstream ss;
ss << myFile.rdbuf(); //Read the file into the stringstream.
std::string fileContents = ss.str(); //Now you have a string, no loops!
段念尘 2024-10-23 22:09:54

你正在浪费内存......它的读取超过了你定义的3个字符(它的读取直到遇到空格或新行......)。

逐个字符地读取以实现您提到的输出。

编辑:激怒是对的,这也有效(经过一些修复,但没有得到确切的结果,但这就是精神):

char ifsw1w[4];
    do{
        ifsw1.width(4);
        ifsw1 >> ifsw1w;
        if(ifsw1.eof()) break;
        cout << ifsw1w << flush << endl;
    }while(1);
    ifsw1.close();

You're trashing the memory... its reading past the 3 chars you defined (its reading until a space or a new line is met...).

Read char by char to achieve the output you had mentioned.

Edit : Irritate is right, this works too (with some fixes and not getting the exact result, but that's the spirit):

char ifsw1w[4];
    do{
        ifsw1.width(4);
        ifsw1 >> ifsw1w;
        if(ifsw1.eof()) break;
        cout << ifsw1w << flush << endl;
    }while(1);
    ifsw1.close();
-黛色若梦 2024-10-23 22:09:54

该代码具有未定义的行为。当您执行以下操作时:

char ifsw1w[3];

ifsw1 >> ifsw1w;

operator>> 收到指向缓冲区的指针,但不知道缓冲区的实际大小。因此,它无法知道它应该在两个字符后停止读取(请注意,它应该是 2,而不是 3 - 它需要 '\0' 的空间来终止字符串)。

底线:在探索读取数据的方法时,最好忽略此代码。您可以从这样的代码中学到的所有内容是您应该避免的一些事情。然而,通常只遵循一些经验法则比尝试研究所有可能出现的问题更容易。

  1. 使用 std::string 读取字符串。
  2. 仅对固定大小的数据使用固定大小的缓冲区。
  3. 当您确实使用固定缓冲区时,请传递它们的大小以限制读取量。
  4. 当你想读取一个文件中的所有数据时,std::copy可以避免很多错误:

    std::vector;字符串;   
    std::copy(std::istream_iterator(myFile),
              std::istream_iterator(),
              std::back_inserter(字符串));
    

The code has undefined behavior. When you do something like this:

char ifsw1w[3];

ifsw1 >> ifsw1w;

The operator>> receives a pointer to the buffer, but has no idea of the buffer's actual size. As such, it has no way to know that it should stop reading after two characters (and note that it should be 2, not 3 -- it needs space for a '\0' to terminate the string).

Bottom line: in your exploration of ways to read data, this code is probably best ignored. About all you can learn from code like this is a few things you should avoid. It's generally easier, however, to just follow a few rules of thumb than try to study all the problems that can arise.

  1. Use std::string to read strings.
  2. Only use fixed-size buffers for fixed-size data.
  3. When you do use fixed buffers, pass their size to limit how much is read.
  4. When you want to read all the data in a file, std::copy can avoid a lot of errors:

    std::vector<std::string> strings;   
    std::copy(std::istream_iterator<std::string>(myFile),
              std::istream_iterator<std::string>(),
              std::back_inserter(strings));
    
澜川若宁 2024-10-23 22:09:54

要读取空格,您可以使用“noskipws”,它不会跳过空格。

ifsw1 >> noskipws >> ifsw1w;

但如果你只想获取3个字符,我建议你使用get方法:

ifsw1.get(ifsw1w,3);

To read the whitespace, you could used "noskipws", it will not skip whitespace.

ifsw1 >> noskipws >> ifsw1w;

But if you want to get only 3 characters, I suggest you to use the get method:

ifsw1.get(ifsw1w,3);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文