C++. Сборник рецептов - Д. Стефенс
Шрифт:
Интервал:
Закладка:
out << 'n' << tmp; // 4.1
i = tmp.length();
tmp.clear();
} else if (isspace(cur) && // Это конец
!isspace(last)) { // слова
out << tmp;
tmp.clear();
}
tmp += cur;
last = cur;
}
}
int main(int argc, char** argv) {
if (argc < 3)
return(EXIT_FAILURE);
int w = 72;
ifstream in(argv[1]);
ofstream out(argv[2]);
if (!in || !out)
return(EXIT_FAILURE);
if (argc == 4) w = atoi(argv[3]);
textWrap(in, out, w);
out.close();
if (out)
return(EXIT_SUCCESS);
else
return(EXIT_FAILURE);
}
ОбсуждениеtextWrap читает по одному символы из входного потока. Каждый символ добавляется к временной строке tmp до тех пор, пока не будет достигнут конец слов или максимальная длина строки. Если достигнут конец слова, а максимальная длина строки еще не достигнута, то временная строка записывается в выходной поток. В противном случае, если максимальная длина строки была превышена, в выходной поток записывается новая строка, пробел в начале временной строки удаляется, и строка записывается в выходной поток. Таким образом, textWrap записывает в выходной поток столько, сколько можно, но не превышая максимальной длины строки. Вместо разделения слов она переносит все слово на новую строку.
Пример 4.25 использует потоки почти так же, как и рецепт 4.15. За дополнительной информацией о потоках и их использовании обратитесь к этому рецепту.
Смотри такжеРецепт 4.15.
4.17. Подсчет числа символов, слов и строк в текстовом файле
ПроблемаТребуется подсчитать число символов, слов и строк — или каких-либо других элементов текста — в текстовом файле.
РешениеДля чтения символов по одному используйте входной поток и по мере чтения символов, слов и строк увеличивайте счетчики. Пример 4.26 содержит функцию countStuff, которая именно это и делает.
Пример 4.26. Подсчет статистики по текстовому файлу
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cctype>
using namespace std;
void countStuff(istream& in,
int& chars, int& words, int& lines) {
char cur = ' ';
char last = ' ';
chars = words = lines = 0;
while (in.get(cur)) {
if (cur == 'n' ||
(cur == 'f' && last == 'r'))
lines++;
else chars++;
if (!std::isalnum(cur) && // Это конец
std::isalnum(last)) // слова
words++;
last = cur;
}
if (chars > 0) { // Изменить значения слов
if (std::isalnum(last)) // и строк для специального
words++; // случая
lines++;
}
}
int main(int argc, char** argv) {
if (argc < 2)
return(EXIT _FAILURE);
ifstream in(argv[1]);
if (!in)
exit(EXIT_FAILURE);
int c, w, l;
countStuff(in, c, w, l);
cout << "символов: " << c << 'n';
cout << "слов: " << w << 'n';
cout << "строк: " << l << 'n';
}
ОбсуждениеЭтот алгоритм очень прост. С символами все просто: увеличивайте счетчик символов при каждом вызове get для входного потока. Со строками все не намного сложнее, так как способ представления концов строк зависит от операционной системы. К счастью, обычно это либо символ новой строки (n), либо последовательность из символов возврата каретки и перевода строки (rn). Отслеживая текущий и предыдущий символы, можно легко обнаружить вхождения этой последовательности. Со словами все проще или сложнее, в зависимости от определения того, что такое «слово».
Для примера 4.26 я предположил, что слово это неразрывная последовательность буквенно-цифровых символов. В процессе просмотра каждого символа входного потока при обнаружении неалфавитно-цифрового символа я проверяю предыдущий символ — был ли он буквенно-цифровым или нет. Если был то это конец слова, и я увеличиваю счетчик слов. Определить, является ли символ буквенно-цифровым, можно с помощью функции isalnum из <cctype>. Но это еще не все — с помощью аналогичных функций можно проверять символы на целый ряд других качеств. Функции, которые предназначены для проверки характеристик символов, приведены в табл. 4.3. Для широких символов используйте функции с такими же именами, но с буквой «w» после «is», например iswSpace. Версии для широких символов объявлены в заголовочном файле <cwctype>.
Табл. 4.3. Функции для проверки символов из <cctype> и <cwctype>
Функция Описание isalpha iswalpha Буквенные символы: a-z, A-Z (верхний или нижний регистр) isupper iswupper Буквенные символы верхнего регистра: A-Z islower iswlower Буквенные символы нижнего регистра: a-z isdigit iswdigit Числовые символы: 0-9 isxdigit iswxdigit Шестнадцатеричные числовые символы: 0-9, a-f, A-F isspace iswspace Пробельные символы. ' ', n, t, v, r, l iscntrl iswcntrl Управляющие символы: ASCII 0-31 и 127 ispunct iswpunct Символы пунктуации, не принадлежащие предыдущим группам isalnum iswalnum isalpha или isdigit равны true isprint iswprint Печатаемые символы ASCII isgraph iswgraph isalpha, isdigit или ispunct равны trueПосле того как были прочтены все символы и достигнут конец файла, требуется сделать еще кое-что. Во-первых, строго говоря, цикл подсчитывает только переносы строк, а не сами строки. Следовательно, это значение будет на одну меньше, чем реальное число строк. Чтобы решить эту проблему, я, если файл содержит ненулевое число символов, просто увеличиваю счетчик строк на единицу. Во-вторых, если поток заканчивается на буквенно-цифровой символ, то поиск конца последнего слова не сработает, так как не будет следующего символа. Чтобы учесть это, я проверяю, является ли последний символ потока буквенно-цифровым (также только в том случае, если в файле содержится ненулевое число символов), и увеличиваю счетчик слов на единицу.
Методика использования потоков в примере 4.26 почти идентична той, которая описана в рецептах 4.14 и 4.15, но несколько проще, так как он только исследует файл, не внося никаких изменений.
Смотри такжеРецепты 4.14 и 4.15.
4.18. Подсчет вхождений каждого слова в текстовом файле
ПроблемаТребуется подсчитать количество вхождений в текстовом файле каждого слова.
РешениеДля чтения из текстового файла непрерывных фрагментов текста используйте operator>>, определенный в <string>, а для сохранения каждого слова и его частоты в файле используйте map, определенный в <map>. Пример 4.27 демонстрирует, как это делается.
Пример 4.27. Подсчет частоты слов
1 #include <iostream>
2 #include <fstream>
3 #include <map>
4 #include <string>
5
6 typedef std::map<std::string, int> StrIntMap;
7
8 void countWords(std::istream& in, StrIntMap& words) {
9
10 std::string s;
11
12 while (in >> s) {
13 ++words[s];
14 }
15 }
16
17 int main(int argc, char** argv) {
18
19 if (argc < 2)
20 return(EXIT_FAILURE);
21
22 std::ifstream in(argv[1]);
23
24 if (!in)
25 exit(EXIT_FAILURE);
26
27 StrIntMap w;
28 countWords(in, w);
29
30 for (StrIntMap::iterator p = w.begin();
31 p != w.end(); ++p) {
32 std::cout << p->first << " присутствует "
33 << p->second << " раз.n";
34 }
35 }
ОбсуждениеПример 4.27 кажется вполне простым, но в нем делается больше, чем кажется. Большая часть тонкостей связана с map, так что вначале давайте обсудим его.