Albay
Albay

Reputation: 21

Difficulties with string declaration/reference parameters (c++)

Last week I got an homework to write a function: the function gets a string and a char value and should divide the string in two parts, before and after the first occurrence of the existing char.

The code worked but my teacher told me to do it again, because it is not well written code. But I don't understand how to make it better. I understand so far that defining two strings with white spaces is not good, but i get out of bounds exceptions otherwise. Since the string input changes, the string size changes everytime.

#include <iostream>
#include <string>
using namespace std;

void divide(char search, string text, string& first_part, string& sec_part) 
{
    bool firstc = true;
    int counter = 0;

    for (int i = 0; i < text.size(); i++) {
        if (text.at(i) != search && firstc) {
            first_part.at(i) = text.at(i);
        }
        else if (text.at(i) == search&& firstc == true) {
            firstc = false;
            sec_part.at(counter) = text.at(i);
        }
        else {
            sec_part.at(counter) = text.at(i);
            counter++;
        }
    }
}

int main() {
    string text;
    string part1="                            ";
    string part2="                            ";
    char search_char;   

    cout << "Please enter text? ";
    getline(cin, text);

    cout << "Please enter a char: ? ";
    cin >> search_char;

    divide(search_char,text,aprt1,part2);
    cout << "First string: " << part1 <<endl;
    cout << "Second string: " << part2 << endl;

    system("PAUSE");
    return 0;
}

Upvotes: 2

Views: 89

Answers (5)

dev15
dev15

Reputation: 750

Your code will only work as long the character is found within part1.length(). You need something similar to this:

void string_split_once(const char s, const string & text, string & first, string & second) {
    first.clear();
    second.clear();
    std::size_t pos = str.find(s);
    if (pos != string::npos) {
        first  = text.substr(0, pos);
        second = text.substr(pos);
    }

}

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310940

As you understand yourself these declarations

string part1="                            ";
string part2="                            ";

do not make sense because the entered string in the object text can essentially exceed the both initialized strings. In this case using the string method at can result in throwing an exception or the strings will have trailing spaces.

From the description of the assignment it is not clear whether the searched character should be included in one of the strings. You suppose that the character should be included in the second string.

Take into account that the parameter text should be declared as a constant reference.

Also instead of using loops it is better to use methods of the class std::string such as for example find.

The function can look the following way

#include <iostream>
#include <string>

void divide(const std::string &text, char search, std::string &first_part, std::string &sec_part)
{
    std::string::size_type pos = text.find(search);

    first_part = text.substr(0, pos);

    if (pos == std::string::npos)
    {
        sec_part.clear();
    }
    else
    {
        sec_part = text.substr(pos);
    }
}

int main()
{   
    std::string text("Hello World");
    std::string first_part;
    std::string sec_part;

    divide(text, ' ', first_part, sec_part);

    std::cout << "\"" << text << "\"\n";
    std::cout << "\"" << first_part << "\"\n";
    std::cout << "\"" << sec_part << "\"\n";
}

The program output is

"Hello World"
"Hello"
" World"

As you can see the separating character is included in the second string though I think that maybe it would be better to exclude it from the both strings.

An alternative and in my opinion more clear approach can look the following way

#include <iostream>
#include <string>
#include <utility>

std::pair<std::string, std::string> divide(const std::string &s, char c)
{
    std::string::size_type pos = s.find(c);

    return { s.substr(0, pos), pos == std::string::npos ? "" : s.substr(pos) };
}

int main()
{   
    std::string text("Hello World");

    auto p = divide(text, ' ');

    std::cout << "\"" << text << "\"\n";
    std::cout << "\"" << p.first << "\"\n";
    std::cout << "\"" << p.second << "\"\n";
}

Upvotes: 1

Christophe
Christophe

Reputation: 73366

Why is your teacher right ?

The fact that you need to initialize your destination strings with empty space is terrible:

  • If the input string is longer, you'll get out of bound errors.
  • If it's shorter, you got wrong answer, because in IT and programming, "It works " is not the same as "It works".

In addition, your code does not fit the specifications. It should work all the time, independently of the current value which is stored in your output strings.

Alternative 1: your code but working

Just clear the destination strings at the beginning. Then iterate as you did, but use += or push_back() to add chars at the end of the string.

void divide(char search, string text, string& first_part, string& sec_part) 
{
    bool firstc = true;
    first_part.clear();   // make destinations strings empty
    sec_part.clear();  

    for (int i = 0; i < text.size(); i++) {
        char c = text.at(i); 
        if (firstc && c != search) {
            first_part += c;
        }
        else if (firstc && c == search) {
            firstc = false;
            sec_part += c;
        }
        else {
            sec_part += c;
        }
    }
}

I used a temporary c instead of text.at(i) or text\[i\], in order to avoid multiple indexing But this is not really required: nowadays, optimizing compilers should produce equivalent code, whatever variant you use here.

Alternative 2: use string member functions

This alternative uses the find() function, and then constructs a string from the start until that position, and another from that position. There is a special case when the character was not found.

void divide(char search, string text, string& first_part, string& sec_part) 
{
    auto pos = text.find(search); 
    first_part = string(text, 0, pos);
    if (pos== string::npos) 
        sec_part.clear(); 
    else sec_part = string(text, pos,  string::npos); 
}

Upvotes: 1

MRB
MRB

Reputation: 3812

I would suggest you, learn to use c++ standard functions. there are plenty utility function that can help you in programming.

void divide(const std::string& text, char search, std::string& first_part, std::string& sec_part)
{
    std::string::const_iterator pos = std::find(text.begin(), text.end(), search);

    first_part.append(text, 0, pos - text.begin());
    sec_part.append(text, pos - text.begin());
}

int main()
{
    std::string text = "thisisfirst";
    char search = 'f';

    std::string first;
    std::string second;

    divide(text, search, first, second);
}

Here I used std::find that you can read about it from here and also Iterators.

You have some other mistakes. you are passing your text by value that will do a copy every time you call your function. pass it by reference but qualify it with const that will indicate it is an input parameter not an output.

Upvotes: 1

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29023

The biggest problem I see is that you are using at where you should be using push_back. See std::basic_string::push_back. at is designed to access an existing character to read or modify it. push_back appends a new character to the string.

divide could look like this :

void divide(char search, string text, string& first_part,
            string& sec_part)
{
    bool firstc = true;

    for (int i = 0; i < text.size(); i++) {
        if (text.at(i) != search && firstc) {
            first_part.push_back(text.at(i));
        }
        else if (text.at(i) == search&& firstc == true) {
            firstc = false;
            sec_part.push_back(text.at(i));
        }
        else {
            sec_part.push_back(text.at(i));
        }
    }
}

Since you aren't handling exceptions, consider using text[i] rather than text.at(i).

Upvotes: 0

Related Questions