2

I am supposed to ask the user for two file names (input and output files). The contents from the input file should be read and the first letter of each sentence should be made uppercase while every other letter should be made lowercase. The results should then be stored in the output file.

I am aware that there are ways of using the toupper and tolower functions that include pointers, arrays, or even ASCII values of chars but I am trying to get this code to work by using if/else and while statements, as well as boolean statements. I have had various results ranging from all the letters being capitalized to none of the letters being capitalized however, I think right now I am on the right track with the code and am just overlooking the way I am incrementing through the characters causing the code to not capitalize after a period and a space.

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

int main() {
    string input_file;  // To hold input file name
    string output_File; // To hold output file name
    char ch;            // To hold character
    fstream inputFile;
    fstream outputFile;

    bool new_sentence = true;

    cout << "Enter input file name: " << endl;
    cin >> input_file;

    cout << "Enter output file name: " << endl;
    cin >> output_File;

    outputFile.open(output_File, ios::out);
    inputFile.open(input_file, ios::in);

    if (inputFile) {
        while (inputFile.get(ch)) {
            if (isprint(ch)) {
                if (new_sentence) {
                    outputFile.put(toupper(ch));
                }
                else {
                    outputFile.put(tolower(ch));
                }
                new_sentence = false;
            }
            else {
                if (ch == '.') {
                   new_sentence = true;
                   outputFile.put(ch);
                }
            }
        }
        inputFile.close();
        outputFile.close();
   }
   else {
       cout << "Cannot open file(s)." << endl;
   }

   cout << "\nFile conversion complete." << endl;

   return 0;
}

With my current code I am able to capitalize the first letter of the first sentence and make every other letter lowercase. I am able to store and show the results in the output file. My issue is that the first letter of every other sentence after the first one won't change to uppercase. This makes me think the issue is in this part of the code:

if (new_sentence)
{
  outputFile.put(toupper(ch));
}

else
{
  outputFile.put(tolower(ch));
}

Am I missing something here?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • 1
    Reread your documentation for `isprint`. – 1201ProgramAlarm Aug 03 '19 at 04:25
  • Read entire input file into a stringstream for ease of case-change parsing, empty back to output file. – ark1974 Aug 03 '19 at 04:52
  • It says isprint is a printable character including white space. My logic is that is that if the character is a whitespace then it would proceed to the next statement which is if (new_sentence). Is my logic wrong here or am I looking at this in the wrong way? I've also already tried isalpha, isspace, and ispunct. The output file ends up showing either the data with no spaces whatsoever or multiple periods but no letters. – Sydnie wyatt Aug 03 '19 at 05:04
  • 1
    Please avoid `using namespace std;`. It is considered bad practice. See [Why is “using namespace std;” considered bad practice?](https://stackoverflow.com/q/1452721) – L. F. Aug 03 '19 at 05:11
  • @Sydniewyatt As 1201ProgramAlarm said `std::isprint` doesn't do what you think it does. The `'.'` character falls into that category, thus the `else` part would never be reached. You can easily prove that yourself by stepping throu your code line by line with the debugger. – πάντα ῥεῖ Aug 03 '19 at 05:23
  • You should validate that your input file is open as part of taking the input filename (and the same for the output file). You do that by simply looping continually until a valid filename is obtained and the file is open for reading (or writing). After you validate that a filename was obtained and the `file.is_open()`, simply `break;` that input loop. You cannot simply check for `'.'` as `'?'` and `'!'` are valid sentence-ending punctuation. Using an `int` or `bool` flag to indicate whether you are in a sentence or between sentences (e.g., `in == false`) will help you identify the first letter. – David C. Rankin Aug 03 '19 at 06:20
  • 1
    @ark1974 - the low-level I/O system provides a minimum read buffer of (512 bytes - windows/8192 bytes - Linux) so there is no penalty using `get()`. There isn't anything wrong with reading into a `stringstream`, in this case it just copies from one buffer to another. – David C. Rankin Aug 03 '19 at 07:00

2 Answers2

2

You have a minor logical error.

You first need to check, if the character is a period. This state you need to remember. If then a next character isalpha, then we check, if recently the newsentence flag has been set. In this case, and only in this case, we reset the new sentence flag and convert the character to uppercase.

All other alpha characters will be converted to lowercase. Other charcaters will not be converted.

In your solution you always reset the newsentence flag. Even, if the next print character is a space (Which is most liekly the case).

Please see updated solution:

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

int main() {
    string input_file;  // To hold input file name
    string output_File; // To hold output file name
    char ch;            // To hold character
    fstream inputFile;
    fstream outputFile;

    bool new_sentence = true;

    cout << "Enter input file name: " << endl;
    cin >> input_file;

    cout << "Enter output file name: " << endl;
    cin >> output_File;

    outputFile.open(output_File, ios::out);
    inputFile.open(input_file, ios::in);

    if (inputFile) {
        while (inputFile.get(ch)) {
            if (ch == '.') {
                new_sentence = true;
            }
            if (isalpha(ch)) {
                if (new_sentence) {
                    ch = toupper(ch);
                        new_sentence = false;
                }
                else {
                    ch = tolower(ch);
                }
            }
            outputFile.put(ch);
        }
        inputFile.close();
        outputFile.close();
    }
    else {
        cout << "Cannot open file(s)." << endl;
}

    cout << "\nFile conversion complete." << endl;

    return 0;
}

And then, please see some further improvements:

#include <iostream>
#include <fstream>
#include <string>
#include <cctype>


int main() {
    // Will hold the input and output filename
    std::string filename;  

    // This is our flag to indicate that a new sentence will come
    bool newSentence = true;

    // Get input filename
    std::cout << "Enter input file name: " << "\n";
    std::cin >> filename;
    // And try to open the file
    std::ifstream inFile(filename);

    std::cout << "Enter output file name: " << "\n";
    std::cin >> filename;
    // And try to open the file
    std::ofstream outFile(filename);

    // Only convert, if the input and output file could be opened 
    if (inFile && outFile) {
        char ch;
        while (inFile.get(ch)) {
            if (ch == '.') {
                newSentence = true;
            }
            if (isalpha(ch)) {
                if (newSentence) {
                    ch = toupper(ch);
                    newSentence = false;
                }
                else {
                    ch = tolower(ch);
                }
            }
            outFile.put(ch);
        }
    }
    else {
        std::cout << "Cannot open file(s)\n";
    }
    std::cout << "\nFile conversion program complete\n";
    return 0;
}

And the full blown "C++ with algorithm" solution. Here the conversion, or transformation is done in one statement

#include <iostream>
#include <fstream>
#include <string>
#include <cctype>
#include <algorithm>
#include <iterator>

int main() {
    // Will hold the input and output filename
    std::string filename;  

    // Get input filename
    std::cout << "Enter input file name: " << "\n";
    std::cin >> filename;
    // And try to open the file
    std::ifstream inFile(filename);

    std::cout << "Enter output file name: " << "\n";
    std::cin >> filename;
    // And try to open the file
    std::ofstream outFile(filename);

    // Only convert, if the input and output file could be opened 
    if (inFile && outFile) {
        // Do the conversion
        std::transform(
            std::istreambuf_iterator<char>(inFile), 
            std::istreambuf_iterator<char>(),
            std::ostreambuf_iterator<char>(outFile),
            [newSentence = true](char c) mutable {
                if (c == '.') newSentence = true; 
                if (std::isalpha(c)) 
                    if (newSentence) { 
                        newSentence = false; 
                        c = std::toupper(c); }  
                    else c = std::tolower(c); 
                return c;
            }
        );
    }
    else {
        std::cout << "Cannot open file(s)\n";
    }
    std::cout << "\nFile conversion program complete\n";
    return 0;
}

But if the last solution adds additional value? I am not sure . . .

A M
  • 14,694
  • 5
  • 19
  • 44
  • Your last example will only set the first letter in the first sentence to upper-case as you have no additional logic to set `newSentence = true;` anywhere in the code. Worth noting that lambda capture initializers only available with `-std=c++14` or later. – David C. Rankin Aug 03 '19 at 06:37
  • You are of course correct. I will update it. I think its anyway not the best solution. It mimicks the 2nd solution and is maybe only more complicated. I am using C++17 – A M Aug 03 '19 at 06:48
  • Suggest `if (c == '.') newSentence = true; else if (isalpha(c))...` I like the solution, it truly is a full blown C++ solution -- now how badly that balloons the assembly, I'll have to check, but it is a neat tidy trick `:)` Effort worth the UV. – David C. Rankin Aug 03 '19 at 06:51
  • Ok so for the logical error I see that I was trying to do if (ch=='.') AFTER isalpha(ch) and was trying to use new_sentence in the wrong part of the if statement. I made the changes you suggested and the code is now working as it should. Thanks! – Sydnie wyatt Aug 03 '19 at 16:22
1

This part of your code should be changed:

        // if (isprint(ch)) {
        if (ch != '.') {
            if (new_sentence) {
                outputFile.put(toupper(ch));
            }
            else {
                outputFile.put(tolower(ch));
            }
            new_sentence = false;
        }
        else {
            new_sentence = true;
            outputFile.put(ch);
        }

std::isprint() only checks if the character is printable.


Full code:

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

int main() {
    string input_file;  // To hold input file name
    string output_File; // To hold output file name
    char ch;            // To hold character
    fstream inputFile;
    fstream outputFile;

    bool new_sentence = true;

    cout << "Enter input file name: " << endl;
    cin >> input_file;

    cout << "Enter output file name: " << endl;
    cin >> output_File;

    outputFile.open(output_File, ios::out);
    inputFile.open(input_file, ios::in);

    if (inputFile) {
        while (inputFile.get(ch)) {
            if (ch != '.') {
                if (new_sentence) {
                    outputFile.put(toupper(ch));
                }
                else {
                    outputFile.put(tolower(ch));
                }
                new_sentence = false;
            }
            else {
                new_sentence = true;
                outputFile.put(ch);
            }
        }
        inputFile.close();
        outputFile.close();
   }
   else {
       cout << "Cannot open file(s)." << endl;
   }

   cout << "\nFile conversion complete." << endl;

   return 0;
}
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • I've changed the code so it doesn't include isprint, isalpha, or any other character testing function and I'm still seeing the same results. I also changed the bool statement to say new_sentence = ". " just to see what output I would get and again, I see the same output as before. – Sydnie wyatt Aug 03 '19 at 06:45
  • My new code essentially just has if( new_sentence) {outputFile.put(toupper(ch));} else {outputFile.put(tolower(ch));} – Sydnie wyatt Aug 03 '19 at 06:47
  • @Sydniewyatt Did you forget to put `new_sentence = false;` after the `if` / `else` block? – πάντα ῥεῖ Aug 03 '19 at 06:49
  • No, I still have that portion. I was just showing the if/else block part that I changed. – Sydnie wyatt Aug 03 '19 at 06:52
  • @Sydniewyatt As from your comment you didn't change anything there at all? – πάντα ῥεῖ Aug 03 '19 at 06:56
  • @Sydniewyatt I have posted the full code with my intended changes. Does yours look the same? – πάντα ῥεῖ Aug 03 '19 at 07:33
  • Yes my code says the same thing but with same results. I have been trying to work out the logic of the new sentence flag but have been getting the same results no matter how I change my code. – Sydnie wyatt Aug 03 '19 at 16:08
  • @Sydniewyatt Sorry then, I am pretty sure that this code should work as intended. Your problem seems to be something else. – πάντα ῥεῖ Aug 03 '19 at 16:12