1

I seem to be running into some odd issues with a portion of my code that involves reading a line from an existing file and writing a portion to another file:

Here is an example of what I am seeing (I only want the vowels).

image

Here are my three functions involved with this. I got a little .c_str() happy while trying figure out the cause:

void do_file_magic(string file){
    fstream source;
    string outfile,
               line,
               vowels,
               temp;

    // Name the outfile
    outfile = "vowels_" + file;

    source.open(file);
    if (!source.is_open()) {
        die_a_clean_death("Unable to open source file:", file);
    };

    ofstream destination(outfile);
    if (!destination.is_open()) {
        source.close();
        die_a_clean_death("Unable to open destination file:", file);
    };

    // read source file and output vowels to destination file.
    do {
        for (int i = 1; !source.eof(); i++ ){
            getline(source, line);
            temp = vowels_on_a_line(line) + "\n";
            destination << temp.c_str();
        };
    } while (!source.eof());

    // Close the files
    source.close();
    destination.close();
};

string vowels_on_a_line(string input){
    int input_size = input.size(),
    vowel_counter = 0;

    // this will not be larger than input_size
    char vowelarray[input_size];

    // find the vowels with the vowel test
    for (int i = 0; i < input_size; i++){
        if (vowel_test(input[i])){
            vowelarray[vowel_counter] = input[i];
            vowel_counter++;
        };
    };

    // convert array to string
    string vowels(vowelarray);

    // return the stringi
    printf("%s\n", vowels.c_str());
    return vowels;
};

bool vowel_test(char c){
    if(
        c=='a'||c=='A'||
        c=='e'||c=='E'||
        c=='i'||c=='I'||
        c=='o'||c=='O'||
        c=='u'||c=='U'
    ) {
        return true;
    } else {
        return false;
    };
};

Does anyone see what I am doing wrong here?

Here is terminal output while running this. I note that there is ?U? in lieu of text editor output:

Mac Shell: Homework2/>$ ./Homework2 

Enter a filename:
-----------------
test.txt
oeiuooiaeoeeuaiiieiaeeaiiaoeiea
uueeaoaeaoiouaiueeiiuaeeeueiiueia?U?
uuoeuaeoeiiuoeeauueuiuioiiuieiuuaaoi?
aeueeieoioooeauaieoaiuiaeuaoeuaiiu?U?
oieuuuiiauoeuoiiaeieieiaeuaaiii
UeaooaiuaeuueieieiauiiaIeeoioeieiiue?
aueuuuuaueieuiaaeuaauaeeeaeeeeuee?U?
aauueiuauaiuoeieeoeeooiiuiiaiaueiuuo?
aeieeaiaieouo

auiooiiaeauiaeuuuaeeuouuaaauoua
eueuoeeuiaeeuuaoiiiuoeieuiuaue
iaeeoaoiaieoiauiuauouiaeiaeuuoi
eueeeueeeiueeieuioauieeuaeuaa
?
iaueeiaeeeueuaoeauueuiauiiauiuieuu?U?
eeeueeeoaeuoioiuIauioeieueiua
eeeuiiieaeeoiiuioueiaoaiuaaiiai
aeaiaeiuauaeeauieoiaeeiiuieeeuui??U?
aiuaaaeuaaoooiouiaiaaiueaeuaa
?
aeeeueuouauueauaeeuUeeoieueioioeua?U?
eeaueueiuaaeuuuooeeeiuaeuuiie
auiieuioeuaaaaieieeeeuiaiieieeuIee?U?
aeaieaeiiaeuaoeauieiuaeueieeeeueua?U?
uoueuuuiaai

euaeioaeuieuiaeaiiaiaeoiouaiuaeeeuauue
iauoieuauiuaeauaoeAeeaiiauiuUeaiueoe?
eaiiaeieiuuoueaeeieuuaoeaeeaiuuaea#

iieuoeuaeeaauEiauiieuaoeaiueaaeoi?U?
auiaeaeiaeeuiauioeiieiieaaoeeuioeeu

uieueeeueuieoeuauiieauieeiueaiueuiiU?
eiuuoiuiieooaaiuiauoaeeiuaiaeuio??U?
H?U?
Mac Shell: Homework2/>$
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Robert J
  • 300
  • 1
  • 3
  • 15
  • 2
    Looks to me like you forgot to zero-terminate your `vowelarray` in `vowels_on_a_line`. – Hermann Döppes Sep 13 '16 at 02:43
  • 2
    Apart from rarely being a good Idea to start with, you actually have *two* loops with terminating condition `source.eof()`, **inside each other**. The outer loop will *always* run exactly once. – Hermann Döppes Sep 13 '16 at 02:58
  • Hermann, thanks for the awesome feedback! I put one one as a condition in the do-while loop and another in the second condition of the for loop. Is that what you are referring to with the second comment or were you thinking that I could do something different with this portion of the code. – Robert J Sep 13 '16 at 03:03
  • 1
    Yes, that's what I'm referring to. The `do … while` loop doesn't, well, *loop*. It doesn't do anything. – Hermann Döppes Sep 13 '16 at 03:32
  • You don't check that your `getline` succeeded before using the value that may or may not have been properly read from the input file. – Galik Sep 13 '16 at 03:34

3 Answers3

3

There are multiple bugs in the shown code.

   for (int i = 1; !source.eof(); i++ ){
         getline(source, line);

Checking for eof() in a loop condition is always a bug.

 char vowelarray[input_size];

 // You put something into vowelarray.

 string vowels(vowelarray);

The shown code instantiates the vowelarray. The vowelarray is not initialized. The subsequent code then proceeds to fill the initial contents of vowealarray.

Passing vowelarray to std::string's constructor decays the char array to a const char *.

The const char * parameter to a std::string constructor must be a `\0'-terminated string.

The shown code fails to terminate the string with a \0 byte. As such the resulting string will typically contain random garbage, which is what you're seeing.

You need to increase the maximum size of the array by 1, in order to account for an extra '\0' character:

char vowelarray[input_size+1];

And then properly add a terminating '\0' to the char array, before constructing a std::string from it.

Community
  • 1
  • 1
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • 1
    Or, you can simply pass `vowel_counter` to the `count` parameter of the `string` constructor. That way, you don't have to worry about null-terminating the `vowels` array, and `string` does not have to waste time re-counting the characters when you already know how many there are: `string vowels(vowelarray, vowel_couter);` – Remy Lebeau Sep 13 '16 at 03:00
  • http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong - Thanks Sam! This also solved another issue that I was having for another project. This article is definitely worth a read for anyone else who might be looking at this initial question. – Robert J Sep 13 '16 at 04:56
1

In vowels_on_a_line(), you need to null-terminate the string stored in vowelarray to avoid getting garbage characters at the end of the line.

Jim Lewis
  • 43,505
  • 7
  • 82
  • 96
1

You are not converting your vowels array to a string correctly. You are not null-terminating the array, and not telling the string constructor how many characters are in the array. So it ends up going out of bounds and copying data from surrounding memory until it encounters a null byte (or crashes).

I would suggest not using an array at all (especially since you are also relying on a non-standard compiler extension to allocate the array in the first place). Try something more like this instead:

void do_file_magic(const string &file)
{
    string outfile = "vowels_" + file;
    string line;

    ifstream source(file.c_str());
    if (!source.is_open()) {
        die_a_clean_death("Unable to open source file:", file);
    };

    ofstream destination(outfile.c_str());
    if (!destination.is_open()) {
        die_a_clean_death("Unable to open destination file:", outfile);
    };

    // read source file and output vowels to destination file.
    while (getline(source, line)) {
        destination << vowels_on_a_line(line) << "\n";
    }
}

string vowels_on_a_line(const string &input)
{
    int input_size = input.size(),
    ostringstream oss;

    // find the vowels with the vowel test
    for (int i = 0; i < input_size; i++){
        if (vowel_test(input[i])) {
            oss << input[i];
        }
    }

    // convert array to string
    string vowels = oss.str();

    // return the string
    cout << vowels << "\n";
    return vowels;
};

bool vowel_test(char c)
{
    return (
        (c=='a')||(c=='A')||
        (c=='e')||(c=='E')||
        (c=='i')||(c=='I')||
        (c=='o')||(c=='O')||
        (c=='u')||(c=='U')
    );
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770