0

I'm trying to make a program that reads input from a file (it is named grades.txt) and make an output of (image attached)

Image showing expected program output.

Apparently, I'm converting strings to char arrays and my program's output quite unexpected (image attached) What I'm getting as the outputI have checked twice, the IDE doesn't show any errors as well.

I'm using this as the source code.

#pragma warning(disable:4996)
#include <fstream>
#include <string>
#include <iostream>
using namespace std;

//Author: Hidden for privacy

const int MAXNAME = 20;

int main()
{
    ifstream inData;
    inData.open("grades.txt");
    string rawInputString;

    char name[MAXNAME + 1]; // holds student name 
    float average;          // holds student average

    inData.get(name, MAXNAME + 1);
    while (inData)
    {
        bool firstSpace = false;
        bool secondSpace = false;
        char converter[23];
            getline(inData, rawInputString, '\n');
            strcpy(converter, rawInputString.c_str());
            for (int a = 0; a <= 22; a++) {
                if (converter[a] != ' ') {
                    cout << converter[a];
                }
                if (converter[a] == ' ') {
                    if (!firstSpace) {
                        firstSpace = true;
                        continue;
                    }
                    if (!secondSpace) {
                        secondSpace = true;
                        continue;
                    }
                }
                if (firstSpace) {
                    cout << converter[a];
                    if (secondSpace) {
                        cout << converter[a];
                    }
                }
            }
    }
    inData.close();
    return 0;
}

Here is the grades.txt file:

Adara Starr          94
David Starr          91
Sophia Starr         94
Maria Starr          91
Danielle DeFino      94
Dominic DeFino       98
McKenna DeFino       92
Taylor McIntire      99
Torrie McIntire      91
Emily Garrett        97
Lauren Garrett       92
Marlene Starr        83
Donald DeFino        73

What I've tried: As seen in the source code, I tried to make the program print the output char by char. But apparently, the first few characters printed are not even in the file where the program is taking input from. I know that float average and char array name are uninitiated, but it doesn't affect the rest of the code (too much), so I'm just leaving those there. I tried reading the fstream library to maybe figure out if this was something caused by the ifstream, but that doesn't seem to be the case either.

Y K
  • 25
  • 8
  • Why do you copy the string you read from the file to the array `converter`? That's one source of problems for you, since you have forgotten that C-style strings are called ***null-terminated** strings*. So a string of 23 characters needs space for **24** character to fit the null-terminator. Actually, there's no need for any copying at all, you can use `rawInputString` directly instead. – Some programmer dude Nov 28 '22 at 07:03
  • Also note that `while (inData)` is the same as `while (inData.good())` which is somewhat equivalent to `while (!inData.eof())` [which is wrong](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons). – Some programmer dude Nov 28 '22 at 07:05
  • In reply to the first comment, @Someprogrammerdude, if I were to use just rawInputString, how would I check whitespace at a certain position in a string? (which would be necessary to add spaces between the name and number) – Y K Nov 28 '22 at 07:09
  • I also recommend you do some research about [input string streams](https://en.cppreference.com/w/cpp/io/basic_istringstream) which can help you parse the input into first name, last name, and the number as separate fields. For output into nice columns use [standard manipulators](https://en.cppreference.com/w/cpp/io/manip) like [`std::setw`](https://en.cppreference.com/w/cpp/io/manip/setw). – Some programmer dude Nov 28 '22 at 07:10
  • The input lines are 24 characters long, so `strcpy(converter, rawInputString.c_str());` overwrites memory. You can do `rawInputString[a]`, so you don't even need `converter`. And why do you have both `inData.get` and `getline(inData...`? Stick with the second as it is memory-safer. – Ken Y-N Nov 28 '22 at 07:10
  • 1
    @YK A `std::string` can be used exactly the same way as a `char` array (with the exception of implicit array-to-pointer decay) and in fact offers many more features than a `char` array. – user17732522 Nov 28 '22 at 07:11
  • Wow, I did not know that I can use indexes with strings, thank you for the response, @KenY-N . This information made solving the problem much easier. – Y K Nov 28 '22 at 07:13
  • Of course you need to terminate your loop at `rawInputString.size()` in that case, not at a constant. But terminating at a fixed constant that is not determined from the input is precisely your problem anyway. – user17732522 Nov 28 '22 at 07:13
  • @YK Might be worth researching all the other things you can do with [strings](https://en.cppreference.com/w/cpp/string/basic_string) – john Nov 28 '22 at 07:24

1 Answers1

2

There are a few different problems in your code, but the main ones is that:

  • You start with inData.get(name, MAXNAME + 1) which reads only a part of the first line
  • You iterate over the whole array no matter its string length
  • The character array converter is just too small to fit a full line including the null-terminator

Among the other problems is that you read from the input file before checking its status; You use while (inData) which is not correct; You use a C-style character array when there's no need; And you forget to print a newline.

There are also better ways to handle the input and output, like using an input string stream to parse out the fields, and use standard I/O manipulators for your output.


If we put it all together I would recommend something like this instead:

#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>

int main()
{
    std::ifstream infile("grades.txt");

    std::string inputline;

    // Read line by line from the input, includes error checking
    // Also includes checking if the file was opened or not
    while (std::getline(infile, inputline))
    {
        // An input stream to parse out the data we need
        std::istringstream parser(inputline);

        std::string firstname;
        std::string lastname;
        int         value;

        parser >> firstname >> lastname >> value;

        // Just construct a simple string of the name
        // To simplify "pretty" output
        std::string fullname = firstname + ' ' + lastname;

        // And finally the output with the name in one column and the value in another        
        std::cout << std::left << std::setw(20) << fullname << value << '\n';
    }
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621