Я очень плохо знаком с C ++, и я довольно долго пытался понять, как решить эту проблему. По сути, мне нужно прочитать из файла и найти все экземпляры статьи («a», «A», «an», «aN», «An», «AN», «the», «The», » «,», «,», «,» и затем вставьте прилагательное после этой статьи. Заглавная буква прилагательного должна основываться на слове, которое находится перед статьей. Например, если бы я нашел «акулу», мне нужно было бы сделать ее «счастливой акулой». Может кто-нибудь сказать мне, что будет лучшим способом сделать это? Пока я отказался от множества идей, и это то, что у меня есть сейчас, хотя я не думаю, что смогу сделать это так:
#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;
}
}
}
}
}
Пока что довольно неплохо, хотя, если вам нужно обработать статью в конце строки, у вас могут возникнуть проблемы с выполнением этой строки построчно.
В любом случае, игнорируя эту складку на секунду, после того, как вы сопоставили статью, затем сначала вам нужно получить следующее слово, на котором вы должны основывать свою капитализацию. Затем вам нужно создать новую строковую версию вашего прилагательного, которая имеет правильную прописную букву:
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;
}
}
}
Возвращаясь к морщине, мы проигнорировали. Вы, вероятно, не хотите делать это построчно, а затем токен за токеном, потому что обработка этого особого случая будет ужасной для вас. Вместо этого вы, вероятно, захотите сделать это токен за токеном в одном цикле.
Итак, вам нужно написать вспомогательную функцию или класс, который работает с файлом и может дать вам следующий токен. (Вероятно, такой класс уже есть в STL, я не уверен.) В любом случае, используя ваш ввод / вывод, он может выглядеть примерно так:
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;
};
И ваш основной цикл будет выглядеть так:
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;
}
}
}
Вы, вероятно, хотите вывести остальную часть ввода тоже?
Сначала я предлагаю вам использовать 3 вспомогательные функции для преобразования строковых падежей. Они будут полезны, если вы много работаете с текстом. Здесь они основаны на <algorithm>
но возможны многие другие подходы:
string strtoupper(const string& s) { // return the uppercase of the string
string str = s;
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return str;
}
string strtolower(const string& s) { // return the lowercase of the string
string str = s;
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
}
string strcapitalize (const string& s) { // return the capitalisation (1 upper, rest lower) of the string
string str = s;
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
if (str.size() > 0)
str[0] = toupper(str[0]);
return str;
}
Затем функция полезности для клонирования заглавных букв в слове: она устанавливает прилагательное в нижнем или верхнем регистре или использует его заглавными буквами (1 верхний + остальной нижний), копируя регистр слова ссылки. Он достаточно надежен для обработки пустых слов и слов, которые не являются буквенно-цифровыми:
string clone_capitalisation(const string& a, const string& w) {
if (w.size() == 0 || !isalpha(w[0])) // empty or not a letter
return a; // => use adj as it is
else {
if (islower(w[0])) // lowercase
return strtolower(a);
else return w.size() == 1 || isupper(w[1]) ? strtoupper(a) : strcapitalize(a);
}
}
Все эти функции не меняют исходные строки!
Теперь к main()
Мне не нравится вручную вводить все возможные комбинации верхнего и нижнего регистров статей, поэтому я работаю только в верхнем регистре.
Мне не нравится последовательно просматривать все возможные статьи для каждого слова. Если бы было еще много статей, это было бы не очень эффективно! Поэтому я предпочитаю использовать <set>
:
...
set<string> articles { "A", "AN", "THE" }; // shorter isn't it ?
...
while (getline(rfile, line)) {
istringstream iss(line);
string word;
while (iss >> word) { // loop
cout << word << " "; // output the word in any case
if (articles.find(strtoupper(word))!=articles.end()) { // article found ?
if (iss >> word) { // then read the next word
cout << clone_capitalisation(adj, word) << " " << word << " ";
}
else cout << word; // if case there is no next word on the line...
}
}
cout << endl;
}