Табл. 4.3. Функции для проверки символов из и
| Функция |
Описание |
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>>, определенный в , а для сохранения каждого слова и его частоты в файле используйте map, определенный в . Пример 4.27 демонстрирует, как это делается.
Пример 4.27. Подсчет частоты слов
1 #include
2 #include
3 #include
4 #include
5
6 typedef std::map 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, так что вначале давайте обсудим его.
Если вы не знакомы с map, то вам стоит узнать про него, map — это шаблон класса контейнера, который является частью STL. Он хранит пары ключ-значение в порядке, определяемом std::lessили вашей собственной функцией сравнения. Типы ключей и значений, которые можно хранить в нем, зависят только от вашего воображения. В этом примере мы просто сохраняем stringи int.
В строке 6 я для упрощения читаемости кода использовал typedef.
typedef map StrIntMap;
Таким образом, StrIntMap— это map, который хранит пары string/int. Каждая string— это уникальное слово именно по этой причине я использую ее как ключ, — которое было прочитано из входного потока, а связанное с ней int— это число раз, которое это слово встретилось. Все, что осталось, — это прочитать все слова по одному, добавить их в map, если их там еще нет, и увеличить значение счетчика, если они там уже есть.
Это делает countWords. Основная логика кратка.
while (in >> s) {
++words[s];
}
operator>>читает из левого операнда ( istream) непрерывные отрезки, не содержащие пробелов, и помещает их в правый операнд ( string). После прочтения слова все, что требуется сделать, — это обновить статистику в map, и это делается в следующей строке.
Читать дальше