Im very new to C++ and ive been struggling for quite a while trying to figure out how to do this problem. Basically, i need to read from a file and find all instances of an article ("a","A","an","aN","An","AN","the","The","tHe","thE","THe","tHE","ThE","THE")and then insert an adjective after that article. The adjective's capitalization must be based on the word originally in front of the article. For instance, if i found "a SHARK" i would need to make it "a HAPPY SHARK." Can anyone tell me what the best way to do this would be? So far I've scrapped a lot of ideas and this is what i have now, though i don't think i can do it this way:
#include <iostream>
#include <string>
#include <cctype>
#include <fstream>
#include <sstream>
using namespace std;
void
usage(char *progname, string msg){
cerr << "Error: " << msg << endl;
cerr << "Usage is: " << progname << " [filename]" << endl;
cerr << " specifying filename reads from that file; no filename reads standard input" << endl;
}
int main(int argc, char *argv[])
{
string adj;
string file;
string line;
string articles[14] = {"a","A","an","aN","An","AN","the","The","tHe","thE","THe","tHE","ThE","THE"};
ifstream rfile;
cin >> adj;
cin >> file;
rfile.open(file.c_str());
if(rfile.fail()){
cerr << "Error while attempting to open the file." << endl;
return 0;
}
while(rfile.good()){
getline(rfile,line,'\n');
istringstream iss(line);
string word;
while(iss >> word){
for(int i = 0; i <= 14; i++){
if(word == articles[i]){
cout << word + " " << endl;
}else{
continue;
}
}
}
}
}
So far, pretty good, although if you need to handle an article at the end of a line, then you might be in trouble doing this line by line.
Anyway, ignoring that wrinkle for a second, after you've matched an article, then first you need to get the next word on which you need to base your capitalization. Then you need to create a new string version of your adjective that has the correct capitalization:
string adj_buf; // big enough or dynamically allocate it based on adj
while(iss >> word){
for(int i = 0; i <= 14; i++){
if(word == articles[i]){
cout << word + " ";
iss >> word; // TODO: check return value and handle no more words on this line
adj_buf = adj;
for (j = 0; j < word.size() && j < adj.size(); ++j)
if (isupper(word[j]))
adj_buf[j] = toupper(adj[j]);
else
adj_buf[j] = tolower(adj[j]);
cout << adj_buf + " " + word;
break;
}
}
}
Circling back to the wrinkle we ignored. You probably don't want to do this line by line and then token by token because handling this special case will be ugly in your control. Instead, you probably want to do it token by token in a single loop.
So, you need to write a helper function or class that operates on the file and can give you the next token. (There probably is exactly such a class already in the STL, I'm not sure.) Anyway, using your I/O it might look something like:
struct FileTokenizer
{
FileTokenizer(string fileName) : rfile(fileName) {}
bool getNextToken(string &token)
{
while (!(iss >> token))
{
string line;
if (!rfile.getline(rfile, line, '\n'))
return false;
iss.reset(line); // TODO: I don't know the actual call to reset it; look it up
}
return true;
}
private:
ifstream rfile;
istringstream iss;
};
And your main loop would then look like:
FileTokenizer tokenizer(file);
while (tokenizer.getNextToken(word))
{
for(int i = 0; i <= 14; i++){
if(word == articles[i]){
cout << word + " ";
if (!tokenizer.getNextToken(word))
break;
adj_buf = adj;
for (j = 0; j < word.size() && j < adj.size(); ++j)
if (isupper(word[j]))
adj_buf[j] = toupper(adj[j]);
else
adj_buf[j] = tolower(adj[j]);
cout << adj_buf + " " + word;
break;
}
}
}
You probably want to output the rest of the input too?