-2

So I've been learning about steams and have been experimenting on my own. I was attempting to write a simple program to read the first line of a file only. But I've noticed an issue in Visual Studio 2019. Below is the code snippet.

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

int main()
{
    std::ifstream in("Test.txt", std::ios::binary);
    if (in)
    {
        std::string store;
        std::getline(in, store, '\n');
        std::cout << store;
    }
    return 0;
}

The test.txt file reads:

This
Is
A
Test

In VS19 the output to the console is just " his". In VSCode the output is "This" as it should be. Am I doing anything wrong in VS19 as far as possible configuration? Is it a bug? It was driving me crazy until I tried a different IDE, can't figure out why it would be doing that. Note: It isn't ignoring the first character of the string, it is replacing it with whitespace.

Hex of Test.txt for VS2019

00000000    54 68 69 73 0D 0A 49 73 0D 0A 41 0D 0A 54 65 73 
00000010    74

And VSCode

00000000: 54 68 69 73 0A 49 73 0A 41 0A 54 65 73 74
Avelion114
  • 13
  • 3
  • 1
    Questions seeking debugging help should generally include a [mre] of the problem, which includes a function `main` and all `#include` directives. – Andreas Wenzel Jan 08 '22 at 07:43
  • 2
    Is `string` a custom class? Or are you doing something very bad as including `` and `using namespace std;`. If so: there's your problem. – JHBonarius Jan 08 '22 at 08:48
  • 3
    Out of curiosity, why you're reading a text file in as binary? On some platforms, specifically Windows, line termination translation will be affected by that. It may also be worth your time to actually check that file for a possible BOM (stranger things). – WhozCraig Jan 08 '22 at 08:52
  • I'm guessing you're using a different compiler in vscode? It's basically just a text editor so isn't relevant to your question. Please show a [mre]. My guess would be some sort of encoding issue, especially as you are opening in binary mode – Alan Birtles Jan 08 '22 at 09:35
  • @AlanBirtles if you don't like my answer, write a better one. Anyhow: IMHO we should start teaching newbies braced initialization. E.g. you still see people using uninitialized floats for example. So I'm writing `auto [const] name{type{}};` everywhere nowadays. IMHO it's the modern C++ way. It's not that more (let alone "extremely") long winded than the old/C-style `type [const] name;`, which as I said is not initialized for intrinsic types. AAA – JHBonarius Jan 08 '22 at 09:54
  • 1
    @JHBonarius if the question was answerable I would write an answer. Why is `auto [const] name{type{}};` better than `type [const] name{};` – Alan Birtles Jan 08 '22 at 10:34
  • @AlanBirtles It paves the way for a uniform initialization syntax, for instance when combined with immediately invokes closure object initialization. E.g. `auto my_var{[]{..some init..}()};` instead of `type my_var{[]{..}()}` where you repeat the return type name. (or even worse `type my_var = []{..}();` that might infer some unwanted implicit conversion.). But maybe I'm the only one thinking this way. I'm quite convinced. – JHBonarius Jan 08 '22 at 10:39
  • @AlanBirtles forgot an important pro... It also prevents the common coding mistake: forgetting initialization. E.g. `float f;` compiles fine, `auto f{float};` doesn't. – JHBonarius Jan 08 '22 at 11:18
  • The code is now a [mre] but there's nothing wrong with it, as you're opening the file as binary the binary contents of the file is the missing piece of the puzzle, does it start with a Unicode BOM? How is it encoded? What line endings does it use? My guess would be that the file has Windows line endings so `store` ends with a carriage return which moves the output back to the beginning of the line after printing your string then maybe something else is printing a space, overwriting the `T` – Alan Birtles Jan 09 '22 at 07:00
  • Please provide a [hex dump](https://en.wikipedia.org/wiki/Hex_dump) of your input file, for example by using `hexdump` on linux, or by using a [hex editor](https://en.wikipedia.org/wiki/Hex_editor). We need to know the exact hexadecimal values of every single byte of the file. – Andreas Wenzel Jan 09 '22 at 07:35
  • Both Visual Studio and Visual Studio Code have the ability to provide hex dumps. See [this question](https://stackoverflow.com/questions/1724586/can-i-hex-edit-a-file-in-visual-studio) on how to do this in Visual Studio and [this question](https://stackoverflow.com/questions/38905181/how-do-i-see-a-bin-file-in-a-hex-editor-in-visual-studio-code) for Visual Studio Code. – Andreas Wenzel Jan 09 '22 at 07:54
  • @AndreasWenzel I added the hex dumps for both IDEs. Could you possibly also explain what exactly binary mode does? I've read several posts and I understand all the other flags but I'm still not sure exactly what ios::binary does and how it differs from the others. I've tried experimenting with it and the other modes and I usually get the same results except for this situation. – Avelion114 Jan 10 '22 at 18:25
  • @Avelion114: On Linux and other POSIX-based operating systems, there is no difference between text and binary mode. However, on some operating systems, such as Microsoft Windows, the situation is different: On Windows, line endings are internally stored as `\r\n` (carriage-return followed by line feed), which is `0D 0A` in hexadecimal representation in ASCII. When opening a file in text mode for reading, `\r\n` gets converted to simply `\n`. When opening a file in text mode for writing, `\n` gets converted to `\r\n`. When opening files in binary mode, no conversion takes place. – Andreas Wenzel Jan 11 '22 at 01:40
  • Does the bug disappear of you remove `std::ios::binary` by writing `std::ifstream in("Test.txt");` instead of `std::ifstream in("Test.txt", std::ios::binary);`? – Andreas Wenzel Jan 11 '22 at 02:03
  • According to your posted hex dump for the input file that you are using for Visual Studio 2019, the first line of the file contains `"Thi"` (with a missing `s` character). Therefore, I find it very hard to believe that when using this input file with the code above, you are getting the output `" his"` (with `s` character). I rather suspect that one of the statements that you have made in the question or in the comments section, is inaccurate. Are you sure that you did not modify the code or the input file in some way, after receiving this output? – Andreas Wenzel Jan 11 '22 at 02:15
  • I suggest that you replace the content of the `if` code block with the following: `for ( int i = 0; i < 5; i++ ) std::cout << std::hex << in.get() << '\n';` This will print the values of the first 5 bytes in the file in hexadecimal. If these values do not correspond to what you expect, you may want to verify that you are reading the correct input file. Instead of opening the file with a relative path such as `"Test.txt"`, you may want to use an absolute path, such as `"C:\\Users\\MyUserName\\source\\repos\\MySolutionName\\MyProjectName\\Test.txt"`. – Andreas Wenzel Jan 11 '22 at 02:30
  • @AndreasWenzel I can assure you everything in the provided code is correct. However changing ios::binary to ios::in changes the output to the desired result which is "This". While that fixes my issue as a beginner I'd still like to understand why that is causing the problem in the first place. In VSCode everything seems to work perfectly fine. Feel free to use the same source code in your own VS2019 and tell me if it provides different results. I am very much intrigued by this result and would like to know what is going on so I may have a better understanding of the processes in general. – Avelion114 Jan 11 '22 at 12:02
  • @AndreasWenzel I'd be happy to include a screenshot including the text, source code, hex, and compile result. Like I said I just want to know specifically why I would be getting this result. It doesn't make sense to me even as a novice programmer. – Avelion114 Jan 11 '22 at 12:05
  • @Avelion114: Please try the two things that I mentioned in my last comment. What is the exact output if you replace the code with the code I mentioned? And does the behavior change if you use an absolute path instead of a relative path? – Andreas Wenzel Jan 11 '22 at 12:07
  • @Avelion114: When I test your code with the input specified in your hex dump for Visual Studio 2019, I get the following output in Visual Studio 2017: `Thi`. I get this even when I use `std::ios::binary` (I did not change your code). As previously mentioned, the letter `s` is missing because it is also missing in you hex dump. That is why I find it very hard to believe that the letter `s` exists in your output, and I suspect that this is because the information that you have provided must be inaccurate in some way. Therefore, I need you to perform these additional tests. – Andreas Wenzel Jan 11 '22 at 12:45
  • `"It doesn't make sense to me even as a novice programmer."` -- It does not mase sense to me, as an experienced programmer, either. That is why I strongly suspect that the information that you have provided must be inaccurate in some way. One possible explanation could be that your program is opening a different file than you expect, because when using a relative path, it opens the file in the current working directory. I suspect that the current working directory could be different than you expect, when launching your program within the IDE. That is why I need you to perform these tests. – Andreas Wenzel Jan 12 '22 at 04:43
  • @AndreasWenzel Apologies I did miss that letter in the hex dump, I've updated it to include it. I've double checked all the code included with mine I'm using in VS2019 and it is still outputting ` his`. When I omit the `std::ios::binary` like you suggested it solves the issue. Still not sure why it would be doing that in the first place though if you say it works fine on your end. – Avelion114 Jan 12 '22 at 17:45
  • Also forgive my ignorance with the formatting on these comments, still trying to learn and no idea why it's doing it incorrectly! – Avelion114 Jan 12 '22 at 17:51
  • @Avelion114: In [this comment](https://stackoverflow.com/questions/70630463/using-fstreamgetline-in-vs2019-is-giving-me-different-results-than-using-it-wi?noredirect=1#comment124914323_70630463), I requested that you perform two additional tests. I'm afraid that I won't be able to help you, until you tell me the results of these two tests. Please provide me with the exact output of the first test. – Andreas Wenzel Jan 12 '22 at 17:52
  • @Avelion114: Note that links to comments only seem to work reliably the first time you click on them. At least in my browser, you can't click on them a second time, without reloading the page through a non-comment link. This seems to be a problem with Stack Overflow. If you are having trouble finding the comment that I linked to, I can write the comment again. – Andreas Wenzel Jan 12 '22 at 18:07
  • @Avelion114: Another test you could perform would be to simply write the following in an otherwise empty function `main`: `std::cout << "This\r";` What output do you see? Can you see the `T`? – Andreas Wenzel Jan 12 '22 at 18:24
  • Actually, regarding my comment in which I asked you to replace the content of the `if` block with `for ( int i = 0; i < 5; i++ ) std::cout << std::hex << in.get() << '\n';`, it would actually be easier if you replaced it with `for ( int i = 0; i < 5; i++ ) std::cout << std::hex << in.get() << ' ';` instead. The second version will produce output text that is easier to paste into a comment, because it contains no newline characters. Please copy&paste the exact output that you get into a comment for me. Please do this with your code as posted, i.e. the one which uses `std::ios::binary`. – Andreas Wenzel Jan 12 '22 at 18:35
  • @AndreasWenzel in regards to your for loop test here is the output `54 68 69 73 d`. I also used the full file path and the output is still ` his`. – Avelion114 Jan 12 '22 at 18:58
  • Fascinating enough, using `std::cout<< "This\r";` also outputs ` his`. I can not see the T – Avelion114 Jan 12 '22 at 18:59
  • @Avelion114: That is very interesting. The problem seems to have nothing to do with file I/O, but simply how your console interprets the `\r` character. Mystery solved. – Andreas Wenzel Jan 12 '22 at 19:04
  • @AndreasWenzel Is there anyway to fix it?? – Avelion114 Jan 12 '22 at 19:09
  • @Avelion114: I wouldn't call it broken. Traditionally, an end of line consists of a carriage-return followed by a line feed character. The carriage-return character causes the cursor to go back to the start of the line, whereas the line feed character causes the cursor to jump to the next line. It appears that your console also overwrites the first character when you jump to the start of line line using carriage-return. I don't see anything wrong with that. – Andreas Wenzel Jan 12 '22 at 19:13
  • @Avelion114: The proper "fix" is to not write a carriage-return to the console in the first place. As you have already determined, if you open the file in text mode (which you should always do with text files), then you will not read a carriage-return (`'r'`) character, but only a newline (line-feed) character. – Andreas Wenzel Jan 12 '22 at 19:17
  • @AndreasWenzel Well thanks for the info then! I'll remember that going forward. I appreciate your time and patience – Avelion114 Jan 12 '22 at 19:23

1 Answers1

0

You are opening your input file in binary mode. This means that getline will read the following bytes (it will discard the newline character):

54 68 69 73 0D

This corresponds to the following string:

"This\r"

The last character is the carriage-return character.

In the comments section of the question, you stated that you experienced the same problem when you simply wrote

std::cout << "This\r";

to the console. In that case, the character T was also being overwritten. This probably means that this is simply the way that your console handles the carriage-return character.

The best way to fix this problem is to open the text file in text mode, instead of binary mode. That way, the "\r\n" line endings will automatically be converted to "\n" line endings, and you won't have to deal with any carriage-return characters.

Therefore, I suggest that you change the line

std::ifstream in("Test.txt", std::ios::binary);

to:

std::ifstream in("Test.txt");
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39