Two Same Strings Don't Equal Each Other?

Pages: 12
I'm inputting a string and comparing it to a string. For some reason they don't equal even though they are the same. I'm guessing this has something to do with \r or \n but I'm not sure.

The only code you should need to look at is this function in my MainFunctions.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void printAPlayersData(std::vector<Player> &PlayersVec) {
    std::string aPlayer = "", checkedName = "";

    // Asks for input
    std::cout << "Which player would you like to print data for?\n";
    std::cout << "Type their full name (CaSE-SenSiTiVE): ";
    std::cin.ignore(1000, '\n');
    std::getline(std::cin, aPlayer);

    unsigned int vectorSize = PlayersVec.size();
    
    // Finds the name in vector (if it is in vector)
    for (unsigned int i = 0; i < vectorSize; i++) {
        checkedName = PlayersVec[i].getPName();
        std::cout << "[Debug] for (unsigned int i = 0; i < vectorSize; i++) ran.\n";
        std::cout << "[Debug] aPlayer = " << aPlayer << '\n'; // they look the same when outputted. Spelled correctly and correct CaSEs.
        std::cout << "[Debug] checkedName = " << checkedName << '\n';
        /*
        =================================================================
        Issue is with the if statement below.
        =================================================================
        */
        if (aPlayer == checkedName) {
            std::cout << "[Debug] if (aPlayer == checkedName) ran.\n";
            std::cout << std::setfill('-') << std::setw(69) << '\n';
            std::cout << "Player Data:\n";
            std::cout << PlayersVec[i].getPName() << '\n'
                    << PlayersVec[i].getTName() << '\n'
                    << PlayersVec[i].getPnts() << '\n'
                    << PlayersVec[i].getRbnds() << '\n'
                    << PlayersVec[i].getAssts() << '\n';
            std::cout << '\n';
        }
    }
}

------------------------------------------------
Main.cpp: http://cpp.sh/6cjr6
MainFunctions.cpp: http://cpp.sh/9z5yw
Player.h (class): http://cpp.sh/6dc4y
Player.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "Player.h"


// Default constructor
// Sets everything to empty / zero.
Player::Player() {
    playerName = "";
    teamName = "";
    points = 0;
    rebounds = 0;
    assists = 0;
}

// Overload constructor
Player::Player(std::string pName, std::string tName, double pnts, double rbnds, double assts) {
    playerName = pName;
    teamName = tName;
    points = pnts;
    rebounds = rbnds;
    assists = assts;
}

// Destructor
Player::~Player() {

}

// Accessor Functions
std::string Player::getPName() const {  return playerName;  }
    // getPName - returns player name

std::string Player::getTName() const {  return teamName;  }
    // getTName - returns team name

double Player::getPnts() const {  return points;  }
    // getPnts - returns points

double Player::getRbnds() const {  return rebounds;  }
    // getRbnds - returns rebounds

double Player::getAssts() const {  return assists;  }
    // getAssts - returns assists

// Mutator Functions
void Player::setPName(std::string pName) {
    playerName = pName;
}

void Player::setTName(std::string tName) {
    teamName = tName;
}

void Player::setPnts(double pnts) {
    points = pnts;
}

void Player::setRbnds(double rbnds) {
    rebounds = rbnds;
}

void Player::setAssts(double assts) {
    assists = assts;
}


Help would be greatly appreciated, thanks!

Just glancing at it I don’t see what is causing the problem.

To be sure: debug the two values like this:

1
2
for (char c : aPlayer)     std::cout << (unsigned)c << " "; std::cout << "\n";
for (char c : checkedName) std::cout << (unsigned)c << " "; std::cout << "\n";

This will clue you in if there are any extra characters in there or alternate encodings you don’t see.

Unrelated notes:
  • You do not need to explicitly set strings to the empty string literal. Strings default to empty. (Line 2 in your first code snippet.)
  • In readFile(), you don’t need to worry about '\r'. All file streams and the console are by default open in “text” mode; CRLF→LF transformations are automatically handled for you behind the scenes.
  • I would personally make the constructor string arguments const std::string&...

If you can’t figure it out, I (or someone else) will probably download your codes and mess with it at some point.
Thank you Duthomhas for your reply and good idea!

I added the lines you suggested to my code. It outputs:
(Please Note: I did the --- line outside of code tags because it would show it incorrectly when I did it inside code tags.)
1
2
Please enter input file name: players.txt
Successfully opened players.txt!

--------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Options:
  1. Add a new player
  2. Display all player data
  3. Display individual profile data
  4. Edit specific player's data (team, points, rebounds, assists)
--------------------------------------------------------------------
Enter the number you would like to do: 3
Which player would you like to print data for?
Type their full name (CaSE-SenSiTiVE): Stephen Curry
[DEBUG]
83 116 101 112 104 101 110 32 67 117 114 114 121

[Debug] for (unsigned int i = 0; i < vectorSize; i++) ran.
[Debug] aPlayer = Stephen Curry
[Debug] checkedName = Stephen Curry
[Debug] for (unsigned int i = 0; i < vectorSize; i++) ran.
[Debug] aPlayer = Stephen Curry
[Debug] checkedName = Lebron James 


Strange. I was expecting to see characters, not their ASCII values. Also, for some reason it only did the first for loop not the second.

How the Function Looks Now:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void printAPlayersData(std::vector<Player> &PlayersVec) {
    std::string aPlayer, checkedName;

    // Asks for input
    std::cout << "Which player would you like to print data for?\n";
    std::cout << "Type their full name (CaSE-SenSiTiVE): ";
    std::cin.ignore(1000, '\n');
    std::getline(std::cin, aPlayer);

    std::cout << "[DEBUG]\n";
    for (char c : aPlayer)     std::cout << (unsigned)c << " "; std::cout << "\n";
    for (char c : checkedName) std::cout << (unsigned)c << " "; std::cout << "\n";

    unsigned int vectorSize = PlayersVec.size();
    
    // Finds the name in vector (if it is in vector)
    for (unsigned int i = 0; i < vectorSize; i++) {
        checkedName = PlayersVec[i].getPName();
        std::cout << "[Debug] for (unsigned int i = 0; i < vectorSize; i++) ran.\n";
        std::cout << "[Debug] aPlayer = " << aPlayer << '\n';
        std::cout << "[Debug] checkedName = " << checkedName << '\n';
        // if the player name inputted is the same as the name in the vecor being checked:
        if (aPlayer == checkedName) {
            std::cout << "[Debug] if (aPlayer == checkedName) ran.\n";
            std::cout << std::setfill('-') << std::setw(69) << '\n';
            std::cout << "Player Data:\n";
            std::cout << PlayersVec[i].getPName() << '\n'
                    << PlayersVec[i].getTName() << '\n'
                    << PlayersVec[i].getPnts() << '\n'
                    << PlayersVec[i].getRbnds() << '\n'
                    << PlayersVec[i].getAssts() << '\n';
            std::cout << '\n';
        }
        // If the player name wasn't found in the vector at all:
        else if (i == vectorSize) {
            std::cout << "[Debug] else if (i == vectorSize) ran.\n";
            std::cout << "The player name you entered was not found in the vector. Did you use proper CAseS?\n";
        }
    }
}


Unrelated notes:
• You do not need to explicitly set strings to the empty string literal. Strings default to empty. (Line 2 in your first code snippet.)

Ah okay. I made the strings empty when I declared them because I've had issues in the past where I tried to add 2 strings but I didn't initialize what the first string had and it caused problems (if I remember correctly). For example:
std::string str1;
std::string str2 = " world";
std::result;

result = str1 + str2;
std::cout << result << '\n';
Although, I just tested the example code above and it works fine. So maybe the issue was something else, I'm not sure...

• In readFile(), you don’t need to worry about '\r'. All file streams and the console are by default open in “text” mode; CRLF→LF transformations are automatically handled for you behind the scenes.

Hmm, well now I'm confused haha. I was told otherwise in this post: http://www.cplusplus.com/forum/beginner/251281/#msg1106678. Also, I was having an issue and it was fixed after ignoring \r for that post.

• I would personally make the constructor string arguments const std::string&...

Okay I changed it. Why though? Also, why not the double arguments too?
Referring to my code from Player.cpp here:
1
2
        // Overload constructor
        Player(const std::string, const std::string, double, double, double);


Thanks!

Edit: Just noticed the & in your comment for the const std::string&. Why did you put a reference (&)?
Last edited on
Why did you put a reference (&)?

So the string being passed into the ctor isn't a copy, the reference passes the address of the string. Creating a copy can be expensive just to use as a parameter is a potential waste of resources.
Strange. I was expecting to see characters, not their ASCII values. Also, for some reason it only did the first for loop not the second.

Yep, ASCII characters are what I wanted to see.

The loop is working fine. You added it in the wrong spot. The print loops should be between lines 18 and 19 in your last snippet.


Hmm, well now I'm confused haha. I was told otherwise in this post:
Ah, you were mis-informed. You misunderstood.

ne555 is sometimes a little short on information, as he assumes that it is obvious. What he meant is that the problem you were having with '\r' is because your text files were not transferred properly between OSs, and you could deal with it by simply finding and removing the '\r' before any other processing.

When copying Windows text files to Unix, make sure you transfer in text mode, or if you are just copying them off a disk, use dos2unix on them.

When copying Unix files to Windows you should perform the opposite transformation.

In neither case should you need to modify your program.


Okay I changed it. Why though? Also, why not the double arguments too?

You didn’t have to, but doing so makes your function more friendly to the world.

1
2
3
4
void hello( const std::string& name = "world" )
{
  std::cout << "Hello " << name << "!\n";
}
1
2
3
4
5
6
// This works great no matter how I declare name:
std::string name = "Jennifer";
hello( name );

// But this also works because a string can be automatically constructed:
hello( "Hayden" );

It also avoids unnecessary copies, which is a good amount of overhead to eliminate.

A double is not a compound type. It is overkill to pass a reference.


I admit I am enjoying helping you, because you have a brain.
Last edited on
I admit I am enjoying helping you, because you have a brain.

I do too. He doesn't balk and complain when confronted by code he's never seen before as happens with so many others. He asks questions, he tries to understand it!

And I can't complain that I learn new ways to do things as well. :)
Last edited on
Thank you Furry Guy and Duthomhas for your replies!

Furry Guy, for your response:
So the string being passed into the [ve]ctor isn't a copy, the reference passes the address of the string. Creating a copy can be expensive just to use as a parameter is a potential waste of resources.
Ah okay. So whenever I'm using a constant string as a parameter in a function then I should always pass by reference to save resources?

Duthomhas also mentioned:
It also avoids unnecessary copies, which is a good amount of overhead to eliminate.

A double is not a compound type. It is overkill to pass a reference.
By compound type do you mean how a string is an array of characters? While a double is just a double?

I checked this post: https://stackoverflow.com/questions/52250480/what-are-compound-types
More specifically, this part of it:
6.7.2 Compound types [basic.compound]

Compound types can be constructed in the following ways:
(1.1) — arrays of objects of a given type;
(1.2) — functions, which have parameters of given types and return void or references or objects of a given type;

------------------------------------------------------
Now, in response to Duthomhas's parts (aside from the part above):

Yep, ASCII characters are what I wanted to see.

The loop is working fine. You added it in the wrong spot. The print loops should be between lines 18 and 19 in your last snippet.
Ahh okay. Yes, moving the code to the lines you mentioned outputs both for loops!

Output:
1
2
Please enter input file name: players.txt
Successfully opened players.txt!
--------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Options:
  1. Add a new player
  2. Display all player data
  3. Display individual profile data
  4. Edit specific player's data (team, points, rebounds, assists)
--------------------------------------------------------------------
Enter the number you would like to do: 3
Which player would you like to print data for?
Type their full name (CaSE-SenSiTiVE): Stephen Curry
[DEBUG]
83 116 101 112 104 101 110 32 67 117 114 114 121
83 116 101 112 104 101 110 32 67 117 114 114 121 13
[Debug] for (unsigned int i = 0; i < vectorSize; i++) ran.
[Debug] aPlayer = Stephen Curry
[Debug] checkedName = Stephen Curry
[DEBUG]
83 116 101 112 104 101 110 32 67 117 114 114 121
76 101 98 114 111 110 32 74 97 109 101 115 13
[Debug] for (unsigned int i = 0; i < vectorSize; i++) ran.
[Debug] aPlayer = Stephen Curry
[Debug] checkedName = Lebron James 

So the ASCII value 13 is the difference between the strings. The checkedName variable has the ASCII value 13 at the end. Based off the ASCII table I think that's "carriage return"? Never heard of that before.
https://www.asciitable.com/

I checked this post: https://stackoverflow.com/questions/3718017/clearing-a-carriage-return-from-memory-in-c
But the solutions appear to only work for cin and not getline(). I use getline() to get player's name in my readFile() function.

readFile() is the function that reads from players.txt (input file) then stores player data into the vector. So I believe that's where I would need to clear the carriage return character before it stores the player names into the vector. You can find my readFile() function in MainFunctions.cpp line 19 in the code below:
Main.cpp: http://cpp.sh/7i5dk
MainFunctions.cpp: http://cpp.sh/3up4e
Player.h: http://cpp.sh/6wrnb
Player.cpp: http://cpp.sh/8wy5m

Ah, you were mis-informed. You misunderstood.

ne555 is sometimes a little short on information, as he assumes that it is obvious.

What he meant is that the problem you were having with '\r' is because your text files were not transferred properly between OSs, and you could deal with it by simply finding and removing the '\r' before any other processing.

When copying Windows text files to Unix, make sure you transfer in text mode, or if you are just copying them off a disk, use dos2unix on them.

When copying Unix files to Windows you should perform the opposite transformation.

In neither case should you need to modify your program.
Ah haha.

What you're explaining makes sense kind of. But I use Windows 10 with Windows Sub Linux (Ubuntu) to gain access to bash. So my files remain as Windows files from my understanding, I'm just using bash to compile and run them. Thus I don't think that info applies to my situation.

You didn’t have to, but doing so makes your function more friendly to the world.

Code example here...

It also avoids unnecessary copies, which is a good amount of overhead to eliminate.

A double is not a compound type. It is overkill to pass a reference.
Ah okay. I already responded to part of this when I was responding to Furry Guy. But for your example, just to confirm:
void hello( const std::string& name = "world" )
sets name to "world" by default. Then if name has another value in main() then it will be that value instead? Wouldn't that make it not constant because it changes from default to a new value?

---------------------------------------------------------
@Duthomhas
I admit I am enjoying helping you, because you have a brain.
Haha (check below for further reply as well)

@Furry Guy
I do too. He doesn't balk and complain when confronted by code he's never seen before as happens with so many others. He asks questions, he tries to understand it!

Thank you both I really appreciate that! <3 :')

I am quite relieved to hear you say that, I've been feeling like you all (everyone who replies to my help posts) were probably getting tired of seeing how many questions I ask. I try to figure out the problem before asking, but often run into problems I can't figure out after a certain period of time and that's when I ask for help.
Last edited on
So the string being passed into the [ve]ctor isn't a copy

I didn't leave off two letters 'cuz I was lazy and really meant to type vector. In this instance ctor is common use short hand for constructor. Just as dtor is common use short hand for destructor.

I got confused when I was first learning C++ too.

So whenever I'm using a constant string as a parameter in a function then I should always pass by reference to save resources?

A std::string is a complex data type, not simple Plain Old Data like an int or double. Copying one is an expensive operation. Using a reference or pointer to pass a string into a function doesn't create a temporary duplicate copy in the function. It passes the address of the string.

Using const as part of the parameter declaration is to prevent the function from potentially modifying the string. The compiler will slap your hand and complain you committed a boo-boo trying to change a string that is not to be changed.

Now, if you WANT to change the string, then don't declare the parameter const.
By compound type do you mean how a string is an array of characters? While a double is just a double?

Exactly.

But! Beware always/never rules. Const& arguments make life easier for the users of the class, not necessarily for the class itself. And there are times when it doesn’t matter.

So the ASCII value 13 is the difference between the strings.
But I use Windows 10 with Windows Sub Linux (Ubuntu) to gain access to bash. So my files remain as Windows files from my understanding, I'm just using bash to compile and run them. Thus I don't think that info applies to my situation.

Ah hah!

Yes, that is exactly the problem. And it is what ne555 was talking about.

ASCII 13 is the code for Carriage Return, which is '\r' in C and C++.

There are a number of ways to fix it, but the simplest is simply to get rid of any trailing CR when you read the string. Use a function:

1
2
3
4
5
6
std::istream& getline2( std::istream& ins, std::string& s )
{
  getline( ins, s );
  if (s.size() and (s.back() == '\r')) s.resize( s.size() - 1 );
  return ins;
}

Use that for getting input instead of getline().


One other point: always resync your inputs as soon as possible. Remember,

The user will always press enter after every requested input.

So, for example, when adding a player:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void addPlayer(std::vector<Player>& PlayersVector) {
    std::string pName, tName;
    double pnts, rbnds, assts;

    // asks for player name
    std::cout << "\n"
              << "Please input the new player's name: ";
    getline2(std::cin, pName);

    // asks for team name
    std::cout << "Please input " << pName << "'s team name: ";
    getline2(std::cin, tName);

    // asks for points
    std::cout << "Please input " << pName << "'s points, rebounds, and assists (separated by spaces): ";
    std::cin >> pnts >> rbnds >> assts;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // resync now!
    
#ifdef DEBUG
    std::cout << "[Debug]\n"
              << pName << '\n'
              << tName << '\n'
              << pnts << '\n'
              << rbnds << '\n'
              << assts << '\n';
#endif
    /*
    Now that we've assigned the input to temp variables,
    we can use the overload constructor of the class to
    set the member variables in class:
    */
    Player tempPlayerObj(pName, tName, pnts, rbnds, assts);

    // Then we can store data into vector:
    PlayersVector.push_back(tempPlayerObj);
}  

Lines 8 and 12 automatically synchronize to the next line.
But line 16 does not, so we have to do it manually on line 17.


Wouldn't that make it not constant because it changes from default to a new value?

Depends on your point of view. ;o)

Inside the function it is const — the function promises not to change it.
Outside the function it is whatever you supply. Including things constructed on-the-fly.

See, if I said void hello( std::string name = "world" ), that would work great... until I tried to call it with:

hello( "Min Na" );

There are reasons. tl;dr: it won’t work. But it will if the argument is a const reference.

Remember, a const is only a contract qualifier that says, “With great (const reference) power comes great responsibility (to not const_cast and change the referenced object)”.


Keep up the good work!


[edit]
You can quote someone by saying

    [quote=Furry Guy]I am not a furry![/quote]

Which produces:

Furry Guy wrote:
I am not a furry!

Last edited on
Holy *BLEEP!* I'm being quoted in context! From my former nick's life!

Dang, I've got to scrub those fleas much harder next time!
I think the biggest problems emerge when we try to use std::cin >> and std::getline in the same code.
I think in general the solution is to avoid std::cin >> unless we can use only it.

Assuming your data are more or less these:
PiggiesGoSqueal.dat:
James Harden
HOU
36.2 6.5 7.5
Paul George
OKC
28.2 8.2 4.2
Stephen Curry
GSW
27.8 5.3 5.2
LeBron James
LAL
27.4 8.6 8.2
Joel Embiid
PHI
27.3 13.7 3.4
Giannis Antetokounmpo
MIL
27.3 12.6 6
Kawhi Leonard
TOR
27.0 7.4 3.3
Kevin Durant
GSW
26.8 6.5 5.7
Damian Lillard
POR
26.4 4.6 6.8
Devin Booker
PHX
26.2 4.2 6.7


The following code does’t give me any problem in a W7 machine, whether the EOL is \r\n, or it is \n:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>


class Player {
public:
    Player() = default;
    Player(std::string name_arg,
           std::string team_arg,
           double points_arg,
           double rebounds_arg,
           double assists_arg);
    Player(const Player& other) = default;
    // Remove & for copy-and-swap idiom:
    Player& operator=(const Player& other) = default;
    Player(Player&& other) = default;
    Player& operator=(Player&& other) = default;

//protected:
    ~Player() = default;

//private:
    std::string name;
    std::string team;
    double points {};
    double rebounds {};
    double assists {};

// friend:
    friend std::ostream& operator << (std::ostream& os, const Player rhs);
};


Player::Player(std::string name_arg,
               std::string team_arg,
               double points_arg,
               double rebounds_arg,
               double assists_arg)
    : name { name_arg }
    , team { team_arg }
    , points { points_arg }
    , rebounds { rebounds_arg }
    , assists { assists_arg }
{
}


std::ostream& operator << (std::ostream& os, const Player rhs)
{
    os <<   "name:     " << rhs.name
       << "\nteam:     " << rhs.team
       << "\npoints:   " << rhs.points
       << "\nrebounds: " << rhs.rebounds
       << "\nassists:  " << rhs.assists;
    return os;
}


std::vector<Player> readDataFromFile(const std::string& fname);
void displaySinglePlayer(const std::vector<Player>& players);


int main()
{
    const std::string fname { "PiggiesGoSqueal.dat" };
    std::vector<Player> players { readDataFromFile(fname) };
    displaySinglePlayer(players);
    return 0;
}


std::vector<Player> readDataFromFile(const std::string& fname)
{
    std::vector<Player> players;
    std::ifstream fin(fname);
    if(!fin) {
        std::cout << "Cannot open " << fname << "!\n";
        return players;
    }

    for(std::string name; std::getline(fin, name) && !name.empty(); /**/) {

        std::string team;
        std::getline(fin, team);

        std::string many;
        std::getline(fin, many);
        std::istringstream iss { many };
        double points, rebounds, assists;
        iss >> points >> rebounds >> assists;

        players.push_back( Player{ name, team, points, rebounds, assists } );
    }

    return players;
}


void displaySinglePlayer(const std::vector<Player>& players)
{
    std::cout << "Please input name-space-surname player: ";
    std::string specimen;
    std::getline(std::cin, specimen);

    auto player { std::find_if(players.begin(),
                               players.end(),
                               [&specimen](const auto& e) {
            return specimen == e.name;
        }) };

    std::cout << "Requested player: " << specimen << '\n'
              << *player << '\n';
}


Output:
Please input name-space-surname player: LeBron James
Requested player: LeBron James
name:     LeBron James
team:     LAL
points:   27.4
rebounds: 8.6
assists:  8.2

You could do this after reading name and team to ensure it works properly on *nix.
1
2
    if (name.size() && name.back() == '\r') name.pop_back();
    if (team.size() && team.back() == '\r') team.pop_back();

dutch wrote:
You could do this

Sorry, dutch, just to understand, who are you addressing to?
@Enoizat, I meant to address it to you. If the file has "\r\n" line endings then using std::getline on *nix would leave the '\r' at the end of the string.
dutch wrote:
@Enoizat, I meant to address it to you.

Ok, thanks.
I usually assume the posts are addressed to the OP, otherwise for them it would become impossible to understand the answers.

dutch wrote:
if (name.size() && name.back() == '\r') name.pop_back();

I agree.
I think that was more or less what Duthomhas suggested in one of the above posts:
Duthomhas wrote:
if (s.size() and (s.back() == '\r')) s.resize( s.size() - 1 );

So many responses! ._.
Thank you everyone for your responses!


Furry Guy wrote:
A std::string is a complex data type, not simple Plain Old Data like an int or double. Copying one is an expensive operation. Using a reference or pointer to pass a string into a function doesn't create a temporary duplicate copy in the function. It passes the address of the string.

Using const as part of the parameter declaration is to prevent the function from potentially modifying the string. The compiler will slap your hand and complain you committed a boo-boo trying to change a string that is not to be changed.

Now, if you WANT to change the string, then don't declare the parameter const.

Ah okay. Also, is it a good programming practice to name const for everything that cannot be changed? Including double, int, etc? So if you're working with other people it makes it easier to tell what is being modified and what isn't. I ask because: http://www.cplusplus.com/forum/beginner/180986/#msg888218

------------------------------------------------
Duthomhas wrote:
Ah hah!

Yes, that is exactly the problem. And it is what ne555 was talking about.

ASCII 13 is the code for Carriage Return, which is '\r' in C and C++.

There are a number of ways to fix it, but the simplest is simply to get rid of any trailing CR when you read the string. Use a function:
1
2
3
4
5
6
std::istream& getline2( std::istream& ins, std::string& s )
{
  getline( ins, s );
  if (s.size() and (s.back() == '\r')) s.resize( s.size() - 1 );
  return ins;
}

Use that for getting input instead of getline().

Ah okay, I thought unix was an operating system, not command prompt. Oops.

For the code, couldn't you not return anything and make the function void because you use a reference, which, from my understanding will update the variable automatically?
For example, if I were to do the following in the readFile() function:
getline2(iFile, pName);
Then it would go through the function, automatically resize pName if needed, then automatically update pName without needing to return it.

Also, is s.size() checking if the string isn't empty, then if it is NOT empty, it will tell the if statement true?

Duthomhas wrote:
One other point: always resync your inputs as soon as possible. Remember,

The user will always press enter after every requested input.

So, for example, when adding a player:
....
Lines 8 and 12 automatically synchronize to the next line.
But line 16 does not, so we have to do it manually on line 17.

Could you please explain what you mean by "resync your inputs"?
Also, could you explain more in depth about what the parts in the line below does:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // resync now!

For the line in the code: #ifdef DEBUG
Wow! That is so useful! Just added ifdefs for Debugging through my code! Thanks a ton!

Somewhat unrelated to the original post, but do you think it's better to include #ifdef DEBUG messages (but DEBUG not defined) for homework assignments and software engineering work? Or only use it for yourself but remove it for the final product you submit to your professor or employer? I think other developers may find it useful when editing your code, but maybe it'll just make your code look messy?

Is it considered good practice to indent between #ifdef and #endif? Or bad practice? Or neither?
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (helloVar == "Hello World") {
	std::cout << "YEAH! Hello to you all as well! <3\n";

	#ifdef DEBUG
		std::cout << "[Debug] if (helloVar == "Hello World") ran.\n";
	#endif
	
	// VS:
	/*
	#ifdef DEBUG
	std::cout << "[Debug] if (helloVar == "Hello World") ran.\n";
	#endif
	*/
}


Dothomhas wrote:
Inside the function it is const — the function promises not to change it.
Outside the function it is whatever you supply. Including things constructed on-the-fly.

[More text]

But it will if the argument is a const reference.

Remember, a const is only a contract qualifier that says, “With great (const reference) power comes great responsibility (to not const_cast and change the referenced object)”.

Keep up the good work!

Ah okay, thanks! (And you as well, for good help)! :P

Duthomhas wrote:
You can quote someone by saying ....

Ahh okay, thanks! I've been wondering if there is a better way to show who I'm responding to!
--------------------------------------------
Enoizat wrote:
I think the biggest problems emerge when we try to use std::cin >> and std::getline in the same code.
I think in general the solution is to avoid std::cin >> unless we can use only it.

Yeah, I think I've been told that in the past but I don't know of a way to get input for numbers other than cin (or iFile >> variableName).

Enoizat wrote:
The following code does’t give me any problem in a W7 machine, whether the EOL is \r\n, or it is \n:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Lots of code]

   for(std::string name; std::getline(fin, name) && !name.empty(); /**/) {

        std::string team;
        std::getline(fin, team);

        std::string many;
        std::getline(fin, many);
        std::istringstream iss { many };
        double points, rebounds, assists;
        iss >> points >> rebounds >> assists;

        players.push_back( Player{ name, team, points, rebounds, assists } );
    }

Looks like a short/easy way to get what I want done. Could you explain what this line does though:
std::istringstream iss { many };
Also how you make these doubles instead of strings: iss >> points >> rebounds >> assists;
Unless you didn't, in which case, I forgot to specify that I'm trying to do it with doubles not strings for number values.


Phew, done replying to everyone. :)

Edit:
By the way, just so everyone's clear where my actual code is at the moment, thanks to your replies it does successfully remove '\r' and print the info for the specific player stated! :)

So the original purpose of this thread has been solved. Just getting some clarification on some parts now.

Current Output:
1
2
Please enter input file name: players.txt
Successfully opened players.txt!

--------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Options:
  1. Add a new player
  2. Display all player data
  3. Display individual profile data
  4. Edit specific player's data (team, points, rebounds, assists)
--------------------------------------------------------------------
Enter the number you would like to do: 3

Which player would you like to print data for?
Type their full name (CaSE-SenSiTiVE): Stephen Curry
--------------------------------------------------------------------
Stephen Curry's Data:
Player Name: Stephen Curry
Team Name: Golden State Warriors
Points: 29.8
Rebounds: 5.2
Assists: 6.5
Last edited on
is it a good programming practice to name const for everything that cannot be changed?

If you are passing an object into a function, either as a copy or as itself by pointer/reference, and you want to indicate the function is not supposed to alter the object, declare the parameter const.

You are setting a contract for you, the programmer, the compiler and other users of your function the object should not be modified.

What went in should come out the same, which doesn't matter much if the object was copied locally.

A devious programmer could get around the request by casting away the constness.

Think "SHOULD NOT" instead of "CAN NOT" be changed.

Dammit, would you stop asking such good questions that make me actually have to work to explain them? :Þ

(No, not really. You asking questions actually is fun for me to try to dig down and dredge up a worthwhile and meaty answer.)
Re: OS vs Command Prompt

When you have the VM up and running, you are running two operating systems at the same time. That has less to do with it than the newline conventions expected by the C library.
The Wikipedia article on Newline is a good read.
https://en.wikipedia.org/wiki/Newline

Er... my head is really fuzzy and I am tired right now. I’ll respond to your other questions sometime tomorrow... Sorry.
Please note my English is poor, so I’m never sure I’ve clarified what I meant to say - that’s why I’m so verbose. But, despite the number of rows, the underlying idea is pretty simple.
- - -
I don't know of a way to get input for numbers other than cin (or iFile >> variableName).

In C++ a std::fstream is a stream.
In C++ std::cin is a stream.
In C++ a std::stringstream is a stream.

std::getline extracts the ‘\n’ character, but then discards it:
https://en.cppreference.com/w/cpp/string/basic_string/getline
b) the next available input character is delim, as tested by Traits::eq(c, delim), in which case the delimiter character is extracted from input, but is not appended to str.

So, if we read a ‘line’ of any stream by std::getline (with the default settings), we ‘clean’ the stream from the final '\n', without taking it along.
Later, we can use that std::string to initialize another stream, like a std::istringstream, and use it exactly the same way we could use std::cin.

Let’s say we have a file dummyfile.txt that contains:
We can't change the direction of the wind, but we can adjust the sails. - Indian Proverb

We read that stream by std::getline:
1
2
3
std::ifstream fin("dummyfile.txt");
std::string line;
std::getline(fin, line);

Now we can initialize another stream by that std::string:
 
std::istringstream another_stream { line };

Now we could say inside “another_stream” there are the same data we could find inside std::cin if we had asked them by this code:
1
2
std::cout << "Please input a sentence: ";
std::cin >> ...

and the user had answered: «We can't change the direction of the wind, but we can adjust the sails. - Indian Proverb».

Yes, we can use the same strategy (perhaps) everywhere. Let’s say we want to input:
John Doe 13 66.6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
std::string line;
// no matter from what stream we are going to read:
// either:
std::cout << "Please input data: ";
std::getline(std::cin, line);
// or:
std::ifstream fin("dummyfile.txt");
std::string line;
std::getline(fin, line);
// In 'line' now there are the same data and the original stream has been
// 'cleared' of the '\n'. Let’s share those data out:
std::istringstream iss { line };
std::string name;
iss >> name;
std::string surname;
iss >> surname;
int civic_number;
iss >> civic_number;
double pocket_money;
iss >> pocket_money;


- - -
Yes, this is definitely too long. My bad.
> For the code, couldn't you not return anything and make the function void
> because you use a reference, which, from my understanding will update the
> variable automatically?
consistency with `getline()', whose prototype is istream& getline (istream& is, string& str);
note that you may use the return value like this
1
2
3
while(getline(input, line)){
	//process the whole file line by line
}



and as you see it (again), printing debugging have its shortcomings, so I'll suggest you to use a debugger to watch your variables.
Pages: 12