Replace space with an underscore
Asked Answered
H

5

12

I am trying to write something that will replace all the spaces in a string with an underscore.

What I have so far.

string space2underscore(string text)
{
    for(int i = 0; i < text.length(); i++)
    {
        if(text[i] == ' ')
            text[i] = '_';
    }
    return text;
}

For the most part this would work, if I was doing something like.

string word = "hello stackoverflow";
word = space2underscore(word);
cout << word;

That would output "hello_stackoverflow", which is just what I want.

However if I was to do something like

string word;
cin >> word;
word = space2underscore(word);
cout << word;

I would just get the first word, "hello".

Does anybody know a fix for this?

Hermosa answered 9/3, 2011 at 21:50 Comment(0)
G
16

The problem is that cin >> word is only going to read in the first word. If you want to operate on a whole like at a time, you should use std::getline.

For example:

std::string s;
std::getline(std::cin, s);
s = space2underscore(s);
std::cout << s << std::endl;

Also, you may want to check that you actually were able to read a line. You can do that like this:

std::string s;
if(std::getline(std::cin, s)) {
    s = space2underscore(s);
    std::cout << s << std::endl;
}

Finally, as a side note, you could probably write your function in a cleaner way. Personally I would write it like this:

std::string space2underscore(std::string text) {
    for(std::string::iterator it = text.begin(); it != text.end(); ++it) {
        if(*it == ' ') {
            *it = '_';
        }
    }
    return text;
}

Or for bonus points, use std::transform!

EDIT: If you happen to be lucky enough to be able to use c++0x features (and I know that's a big if) you could use lambdas and std::transform, which results in some very simple code:

std::string s = "hello stackoverflow";
std::transform(s.begin(), s.end(), s.begin(), [](char ch) {
    return ch == ' ' ? '_' : ch;
});
std::cout << s << std::endl;
Grot answered 9/3, 2011 at 21:53 Comment(6)
Sorry to be pedantic, but you're mixing up whether or not you use std:: or not. (Though technically, your code works with using std::cout; at the top)Aerophone
@Platinum Azure: you are right, personally I like to use an explicit std:: but I copied some of the OP's code in my example. I'll clean that up.Grot
Thank you sir! My program runs fine now :)Hermosa
I really, really like lambdas but in this case std::replace may be a better fit.Nealon
@Blastfurnace: I have to be honest, I completely forgot about std::replace, good call!Grot
@Evan Teran: Whenever I find myself writing yet another for loop I like to check the standard library. There is usually something there that is correct, fast, and with a more meaningful name than for or while. The less code I have to write means fewer bugs, too.Nealon
N
28

You've got your getline issue fixed but I just wanted to say the Standard Library contains a lot of useful functions. Instead of a hand-rolled loop you could do:

std::string space2underscore(std::string text)
{
    std::replace(text.begin(), text.end(), ' ', '_');
    return text;
}

This works, it's fast, and it actually expresses what you are doing.

Nealon answered 9/3, 2011 at 22:47 Comment(1)
don't forget to mention that this needs <algorithm> header file to be included in your project.Weed
G
16

The problem is that cin >> word is only going to read in the first word. If you want to operate on a whole like at a time, you should use std::getline.

For example:

std::string s;
std::getline(std::cin, s);
s = space2underscore(s);
std::cout << s << std::endl;

Also, you may want to check that you actually were able to read a line. You can do that like this:

std::string s;
if(std::getline(std::cin, s)) {
    s = space2underscore(s);
    std::cout << s << std::endl;
}

Finally, as a side note, you could probably write your function in a cleaner way. Personally I would write it like this:

std::string space2underscore(std::string text) {
    for(std::string::iterator it = text.begin(); it != text.end(); ++it) {
        if(*it == ' ') {
            *it = '_';
        }
    }
    return text;
}

Or for bonus points, use std::transform!

EDIT: If you happen to be lucky enough to be able to use c++0x features (and I know that's a big if) you could use lambdas and std::transform, which results in some very simple code:

std::string s = "hello stackoverflow";
std::transform(s.begin(), s.end(), s.begin(), [](char ch) {
    return ch == ' ' ? '_' : ch;
});
std::cout << s << std::endl;
Grot answered 9/3, 2011 at 21:53 Comment(6)
Sorry to be pedantic, but you're mixing up whether or not you use std:: or not. (Though technically, your code works with using std::cout; at the top)Aerophone
@Platinum Azure: you are right, personally I like to use an explicit std:: but I copied some of the OP's code in my example. I'll clean that up.Grot
Thank you sir! My program runs fine now :)Hermosa
I really, really like lambdas but in this case std::replace may be a better fit.Nealon
@Blastfurnace: I have to be honest, I completely forgot about std::replace, good call!Grot
@Evan Teran: Whenever I find myself writing yet another for loop I like to check the standard library. There is usually something there that is correct, fast, and with a more meaningful name than for or while. The less code I have to write means fewer bugs, too.Nealon
A
5

The problem is with your understanding of std::cin from the iostream library: Using the >> operator on a stream with an std::string as the right-hand-side argument only takes one word at a time (using whitespace to separate).

What you want instead is to use std::getline() to get your string.

Aerophone answered 9/3, 2011 at 21:53 Comment(0)
M
2

For a modern C++1x approach you have the option of std::regex_replace.

#include <regex>
#include <string>
#include <cstdlib>
#include <iostream>

using std::cout;
using std::endl;
using std::regex;
using std::string;
using std::regex_replace;

int main( const int, const char** )
{
   const auto target = regex{ " " };
   const auto replacement = string{ "_" };
   const auto value = string{ "hello stackoverflow" };

   cout << regex_replace( value, target, replacement ) << endl;

   return EXIT_SUCCESS;
}

Pros: Less code.

Cons: Regular expressions can cloud intent.

Manichaeism answered 5/5, 2017 at 23:48 Comment(0)
F
-1

Replace

cin >> word;

With

getline(cin, word);
Filibertofilibuster answered 9/3, 2011 at 21:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.