Начинающим программистам полезно поупражняться в решении задачи разбиения предложения на слова.
К решению задачи разбиения предложений на слова могут быть разные требования, в которых, например, могут быть требования запрета использования функции strtok и ей подобных, запрет на использование строковых потоков и другие запреты. Эти ограничения обусловлены тем, что дающий такие задания хочет научить своего студента немного работать с текстом.
Но пока что отложим решения с ограничениями, будем использовать наиболее простые способы:
В простейшем случае в предложении все слова написаны полностью и в учебных целях не нужно предусматривать чуть более сложные случаи, чем простые. Например, если в предложении слова объединяются апострофом, как в английском языке: I’m, то это два слова: I am. Новичкам не нужно на таких моментах запариваться, и имеет смысл выдавать два слова как есть, т. е. слово I и слово m. Что нашли в тексте, о том и сказали, это нормально для новичков.
Самый простой и короткий способ деления предложений на слова — это использование строковых потоков.
Современный стиль:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <sstream>
usingnamespacestd;
intmain(){
constintN=256;//Максимальная длина строки
charS[N]="Hello, i am string\nHow are you?\n-All OK!";//Строка-предложение
charword[N]={};//Буфер для считывания строки
stringstreamx;//Создание потоковой переменной
x<<S;//Перенос строки в поток
while(x>>word)cout<<word<<'\n';//выборка слов
cin.get();
}
Стиль эпохи динозавров:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream.h>
#include <strstream.h>
#include <conio.h>
intmain(){
clrscr();
constintN=256;//Максимальная длина строки
charS[N]="Hello, i am string\nHow are you?\n-All OK!";//Строка-предложение
charword[N];//Буфер для подбора строк
strstreamx;//Создание потоковой переменной
x<<S;//Перенос строки в поток
while(x>>word&& x.good()) cout << word << '\n';//Выборка слов
cin.get();
}
Это самый быстрый для программиста способ написания кода, разбивающего строку на слова, например, для подсчёта количества слов в строке. Сейчас я вывожу слова, но это чтобы было вам видно, что для обработки отдельных слов в дальнейшем требуются дополнительные затраты времени на написание кода, ведь многие ненужные слову символы прилипают к словам, нужно их подчищать.
Принцип работы очень прост: внести в поток, забирать из потока. При отборе у потока данных используется операция <<, из-за неё поток отдаёт все символы, идущие до первого пробела, а сами пробелы игнорируется. Поэтому получается, что в поток вы отдали всю строку, а забираете из потока отдельные слова.
Для того, чтобы работать над словами строки, можно использовать способ, применяемый в языке С: использовать функцию strtok. В таком случае нам можно будет указывать символы-разделители, которыми определяется, что символ делит слово. В самом обычном виде слова делятся знаками препинания: пробел, точка, запятая, восклицательный знак и др. Есть и неудобства, но на некоторые осложняющие случаи чаще все закрывают глаза: например, дефис и тире требует дополнительной обработки, хотя в типографии дефис и тире разные символы, мы часто используем символ дефиса в качестве тире. Я не буду усложнять показываемый код дополнительными обработками, чтобы новички могли понять основное, да и всех случаев возможных проблем я просто не могу предвидеть. Будем считать, что для анализа нам подаются простые строки.
Функция strtok это стандартная функция языка С, она позволяет разбивать строки на части
Чтобы разбить строку на части функцией strtok, нужно функции подсказать набор символов-разделителей, по которым видно, что идёт несколько слов, а не одно.
Для того, чтобы иметь возможность работы с функцие strtok, нужно заинклюдить заголовочный файл string.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Borland C++ 3.1 Разбить строку на слова Листинг #1
charS[N]="Hello, i am string\nHow are you?\n-All OK!";//Строка для анализа
char*ptr=0;//Указатель
ptr=strtok(S,SEPARATORS);//Выдираем первое слово из строки
// cout << ptr << '\n; //Можете убедиться в этом, убрав цикл
while(ptr){//while (ptr != NULL)
cout<<ptr<<'\n';
ptr=strtok(0,SEPARATORS);//Подбираем слово
}
cin.get();
}
Функция strtok берёт строку и ищет в ней вхождение любого указанного символа-разделителя, при нахождении такого символа этот символ подменяется символом-признаком конца строки. Указательная переменная, которая указывает на символьный массив, может выполнять роль строки, любая строка заканчивается нуль-символом, поэтому при выводе на экран символьных массивов мы наблюдаем не массивы целиком, а только их значимую часть, т. е. строку как она есть. Если в любое место строки вписать нуль-символ, то строка как будто бы обрежется:
C++
1
2
3
charS[N]="Hello, i am string\nHow are you?\n-All OK!";//Строка для анализа
S[5]=0;//Обрезаем строку, принудительно установив признак окончания в 5-й символ
cout<<S<<'\n';
Это strtok и делает.
Почему в strtok отдаётся именно ноль для дальнейшего анализа, мне объяснить вам сложно. Скорее всего написавшие функцию программисты немного прогадали с интуитивно-понятным вариантом, решили, что если не другая строка, то пусть будет нулевой указатель. Я усложнять ничего не хочу, поэтому на первое время предлагаю запомнить, что в случае работы с strtok, если нужно продолжать работу с той же строкой, то нужно отдавать нулевой указатель и разделители, а при первом обращении нужно обязательно отдать анализируемую строку и разделители.
Чтобы слова поочерёдно выбирались, мы делаем цикл:
C++
1
2
3
4
5
while(Ptr)
{
cout<<Ptr<<"\n";
Ptr=strtok(0,separator);//Выбираем следующее слово текущей строки
}
Функция strtok разрушает анализируемую строку.
Я уже писал, что функция strtok принудительно пишет символ-признак окончания строки в строку, из-за этого оригинальная строка бьётся.
charS[N]="Hello, i am string\nHow are you?\n-All OK!";//Строка для анализа
char*ptr=0;//Указатель
ptr=strtok(S,SEPARATORS);//Выдираем первое слово из строки
while(ptr){//while (ptr != NULL)
cout<<ptr<<'\n';
ptr=strtok(0,SEPARATORS);
}
cout<<S<<'\n';//Выводим оригинальную строку, что-то не то уже
cin.get();
}
По этой причине вам нужно подумать перед использованием функции strtok, надо ли вам сохранить оригинальное значение строки или не надо. Сохранить строку в дополнительную переменную несложно, важнее, чтобы вы понимали сам факт разрушения оригинальной строки при показанном подходе.
charS[N]="Hello, i am string\nHow are you?\n-All OK!";//Строка для анализа
chartemp_S[N];
strncpy(temp_S,S,strlen(S)+1);//Сохранение строки во временное значение
/*НАЧАЛО АНАЛИЗА СТРОКИ НА СЛОВА*/
char*ptr=0;
ptr=strtok(S,SEPARATORS);
while(ptr){
cout<<ptr<<'\n';
ptr=strtok(0,SEPARATORS);
}
/*КОНЕЦ АНАЛИЗА*/
strncpy(S,temp_S,strlen(temp_S)+1);//Возврат оригинальной строки к оригинальному тексту
cout<<S<<'\n';
cin.get();
}
Когда вы будете смотреть в другие источники, то будете встречать такие термины: лексема и токен. Мне эти термины выносили мозг. Если углубляться в лингвистику, то слово лексема не очень уместно для русского человека из-за наличия падежей в русском языке, а токены — это просто выбираемые по какому-то признаку кусочки текста, в случае со словами токенами оказываются просто слова. Если бы массив состоял из цифр, а делителем была бы цифра 5, то токеном было бы любое число, отделённое символом 5:
3 4 4 3 5 6 2 3 5 3 5 2 1 1 2 5
Токены: 3443, 623, 3, 2112 — ведь словами их трудно назвать.
27 комментариев на «“C++ для начинающих. Строки. Разбить строку на слова”»
я могу показать простой пример, если известно сколько в предложении слов. Т.е. известно сколько переменных нужно объявить в программе.
И этот подход в любом случае идеологически неправильный.
подойдет такой пример?
=====================
немного подрихтуете. пример основан на примере из темы, но в другой среде разработки.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <string.h>
#include <clocale>
constcharseparator[]=" \.,";
constintN=255;
voidinit(charS[N])
{
for(inti=0;i<N;i++)S[i]='\0';
}
intmain()
{
setlocale(LC_ALL,"");
charS[]="Строка, которая будет разбита на отдельные слова ";
charS1[N];
charS2[N];
charS3[N];
init(S1);//делаю переменные-строки пустыми
init(S2);
init(S3);
char*temp=newchar[strlen(S)];
strcpy(temp,S);
intcount_my_words=1;//счетчик считанных слов
char*Ptr=NULL;
Ptr=strtok(S,separator);//Исходная строка будет изменена
while(Ptr)
{
switch(count_my_words)
{
case1:strcpy(S1,Ptr);//прямое присваивание не получится, но если использовать функции работы со строками, то все ок
count_my_words++;//обозначаем, что в первую переменую уже что-то записали
break;//см. тему про switch
case2:strcpy(S2,Ptr);//аналогично
count_my_words++;
break;
case3:strcpy(S3,Ptr);//аналогично
count_my_words++;
break;
}
Ptr=strtok(0,separator);//указываем на новый токен
}
std::cout<<S1<<"\n";//в отдельные переменные было что-то записано
std::cout<<S2<<"\n";
std::cout<<S3<<"\n";
strcpy(S,temp);//это так, если вдруг главная строка нужна в программе, то восстановили.
return0;
}
Если вы под отдельными переменными имели ввиду элементы массива, то извиняйте. Как был задан вопрос, так я и ответил.
Если в предложение неизвестное количество слов и оно может быть разным, то для того, чтобы описывать каждое слово в отдельной переменной, нужно каждую переменную создавать во время выполнения программы. Это работа с указателями и выделением памяти. Но все равно все сводится к тому, что после работы программы надо будет очищать выделенную память своими руками, а чтобы ее очищать, нужно где-то хранить адреса. Объяснять долго. Всё сведется к тому что нужен массив, в котором будут храниться занятые системой адреса для их освобождения после завершения работы программы. Это геморрно, неудобно и неэффективно. Или же надо лезти в деревья (стеки, деревья, очереди 🙂 )
здравствуйте. я быстро не могу помочь никак, да и непонятно зачем пропускали если хотите изучить С++
ваш пример — перегрузка операторов. Перегрузка операторов почти ничего не объясняет в икнапсуляции.
Здравствуйте! Не могли бы вы мне помочь? Как разбить файл(.txt) на части по указанным строкам и записать эти части на отдельные файлы. Помогите пожалуйста…..
Извините, а причём тут С++? Ваш пример — дикая смесь двух языков. Из всего С++ здесь только std::cout всё остальное — С. Почему не используются строковые потоки, кода было бы раз в 5 меньше.
Уважаемые новички! Помните, что если вы компилите .cpp, это ещё не значит, что вы пишете на С++. Если вы не знаете стандартной библиотеки, вы не знаете С++. Если вы не используете виртуальные функции, вы даже не приступили к ООП. Вы используете мощнейший язык на полпроцента.
На занятиях часто ограничивают задания делать без STL и что, это значит учат уже не С++?
Не зная STL не знаешь С++ — дикая чушь. STL не есть С++, С++ способно жить и без STL
C++ — это не ООП язык, а мультипарадигменный язык, он поддерживает ООП и только.
Ну и что, что пример смешан? C++ задумывался как язык, который включает в себя Си.
Здравствуйте, очень интересная статья!
А можно ли как-нибудь записать еще и сами разделители отдельно? Например есть строка «А=В+С» и вывод был бы
А
=
В
+
С
В настройках проекта добавь _CRT_SECURE_NO_WARNINGS
Как это сделать, можно посмотреть, например: https://www.youtube.com/watch?v=NZW7djD3mH8
Как заходить в настройки — дело привычки. Можно как в видео, можно с помощью основного меню:
Project -> Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions» добавить туда _CRT_SECURE_NO_WARNINGS
Как именно добавлять, хорошо видно в видео.
Здравствуйте, подскажите, как выделить из текста предложения и записать их в отдельные переменные массива? Текст берется из файла. Пусть, например, в тексте всего несколько предложений, 3 или 4.
Считывать текст посимвольно. Составлять из считываемого строку до тех пор, пока не обнаружится признак конца предложения. Признак обнаруживается — предложение фиксируется в массиве, строка очищается, индекс обновляется. Нужно использовать вектор или другой динамический массив. С обычным массивом нормально не записать, поскольку заранее неизвестно, сколько предложений будет записано.
Здравствуйте. ПОмогите пожалуйста. Мне нужно выделить слова из строки и проверить их на содержание определенных букв, но как мне выделить слова и поработать над ними?
Спасибо вам огромное. Я тут что-то сам наделал. В итоге я запускаю и почему-то вожу не одну строку, а три, и после ничего не выводиться. Чувствую что-то не так с WORD. Мне нужна длина слова, но я WORD задал как переменная типа char, а чтобы подсчитать длину, нужен тип string, не могу понять. Не подскажите в чем ошибка. Заранее огроменное спасибо.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include
#include
#include
#include
#include
usingnamespacestd;
constintN=256;//Максимальная длина строки
charword[N]={};//Буфер для считывания строки
stringB;
voidcheck(intt,stringword){
t=0;
for(inti=0;i<word.length();i++){
intk=i;
for(intk=i;k>B;
constintN=256;//Максимальная длина строки
charS[N]="Hello i am string How are you All OK";//Строка-предложение
Так уж выходит, что движок сайта ломает Вашу попытку показать мне код. Преобразовать его в исходную форму мне не под силу.
Или разместите код заново, и при этом попробуйте правильно. (код вставляется в исходном виде между тегами [cpp]КОД[/cpp], или попробуйте использовать онлайн компиляторы, в которых компилировать код, после чего брать ссылку и делится ей.
Пример сохранённого кода: https://rextester.com/HEFT80097 (сначала написать код, потом run (выполнить) и save (запомнить), будет создана ссылка, которой можно делиться.
А как разбить строку на слова и записать каждое полученное слово в отдельную переменную?
Покажите, пожалуйста, хотя бы на примере 2х слов.
я могу показать простой пример, если известно сколько в предложении слов. Т.е. известно сколько переменных нужно объявить в программе.
И этот подход в любом случае идеологически неправильный.
подойдет такой пример?
=====================
немного подрихтуете. пример основан на примере из темы, но в другой среде разработки.
Если вы под отдельными переменными имели ввиду элементы массива, то извиняйте. Как был задан вопрос, так я и ответил.
Если в предложение неизвестное количество слов и оно может быть разным, то для того, чтобы описывать каждое слово в отдельной переменной, нужно каждую переменную создавать во время выполнения программы. Это работа с указателями и выделением памяти. Но все равно все сводится к тому, что после работы программы надо будет очищать выделенную память своими руками, а чтобы ее очищать, нужно где-то хранить адреса. Объяснять долго. Всё сведется к тому что нужен массив, в котором будут храниться занятые системой адреса для их освобождения после завершения работы программы. Это геморрно, неудобно и неэффективно. Или же надо лезти в деревья (стеки, деревья, очереди 🙂 )
здравствуйте. я быстро не могу помочь никак, да и непонятно зачем пропускали если хотите изучить С++
ваш пример — перегрузка операторов. Перегрузка операторов почти ничего не объясняет в икнапсуляции.
Инкапсуляция
Инкапсуляция
Как понимать классы в С++ http://ci-plus-plus-snachala.ru/?p=35
Немного о private http://ci-plus-plus-snachala.ru/?p=44
Еще немного о private http://ci-plus-plus-snachala.ru/?p=44
Инкапсуляция — это то, что к private относится (я бы назвал ее приватизацией, т.к. под слово хорошо подходит и произношением и смыслом, но злые технари не любят когда вещи называют не теми именами, которые им даны)
Здравствуйте! Не могли бы вы мне помочь? Как разбить файл(.txt) на части по указанным строкам и записать эти части на отдельные файлы. Помогите пожалуйста…..
на с++
Извините, а причём тут С++? Ваш пример — дикая смесь двух языков. Из всего С++ здесь только std::cout всё остальное — С. Почему не используются строковые потоки, кода было бы раз в 5 меньше.
Уважаемые новички! Помните, что если вы компилите .cpp, это ещё не значит, что вы пишете на С++. Если вы не знаете стандартной библиотеки, вы не знаете С++. Если вы не используете виртуальные функции, вы даже не приступили к ООП. Вы используете мощнейший язык на полпроцента.
На занятиях часто ограничивают задания делать без STL и что, это значит учат уже не С++?
Не зная STL не знаешь С++ — дикая чушь. STL не есть С++, С++ способно жить и без STL
C++ — это не ООП язык, а мультипарадигменный язык, он поддерживает ООП и только.
Ну и что, что пример смешан? C++ задумывался как язык, который включает в себя Си.
Здравствуйте, очень интересная статья!
А можно ли как-нибудь записать еще и сами разделители отдельно? Например есть строка «А=В+С» и вывод был бы
А
=
В
+
С
Помогите, пожалуйста!
у меня vs2013 studio exspress и он ругается на strtok вот так — erorr C4996: ‘strtok’ подскажите что делать?
В настройках проекта добавь _CRT_SECURE_NO_WARNINGS
Как это сделать, можно посмотреть, например: https://www.youtube.com/watch?v=NZW7djD3mH8
Как заходить в настройки — дело привычки. Можно как в видео, можно с помощью основного меню:
Project -> Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions» добавить туда _CRT_SECURE_NO_WARNINGS
Как именно добавлять, хорошо видно в видео.
Здравствуйте, подскажите, как выделить из текста предложения и записать их в отдельные переменные массива? Текст берется из файла. Пусть, например, в тексте всего несколько предложений, 3 или 4.
Считывать текст посимвольно. Составлять из считываемого строку до тех пор, пока не обнаружится признак конца предложения. Признак обнаруживается — предложение фиксируется в массиве, строка очищается, индекс обновляется. Нужно использовать вектор или другой динамический массив. С обычным массивом нормально не записать, поскольку заранее неизвестно, сколько предложений будет записано.
Здравствуйте. ПОмогите пожалуйста. Мне нужно выделить слова из строки и проверить их на содержание определенных букв, но как мне выделить слова и поработать над ними?
Способ 1: При разбивании строки на слова в момент фиксации слова сразу работать со словом.
Способ 2: Запоминать слова в вектор, работать с вектором.
Пример для 1:
Спасибо вам огромное. Я тут что-то сам наделал. В итоге я запускаю и почему-то вожу не одну строку, а три, и после ничего не выводиться. Чувствую что-то не так с WORD. Мне нужна длина слова, но я WORD задал как переменная типа char, а чтобы подсчитать длину, нужен тип string, не могу понять. Не подскажите в чем ошибка. Заранее огроменное спасибо.
длину у char[] можно вычислить используя готовую функцию:
Так уж выходит, что движок сайта ломает Вашу попытку показать мне код. Преобразовать его в исходную форму мне не под силу.
Или разместите код заново, и при этом попробуйте правильно. (код вставляется в исходном виде между тегами [cpp]КОД[/cpp], или попробуйте использовать онлайн компиляторы, в которых компилировать код, после чего брать ссылку и делится ей.
https://rextester.com/l/cpp_online_compiler_clang
Пример сохранённого кода: https://rextester.com/HEFT80097 (сначала написать код, потом run (выполнить) и save (запомнить), будет создана ссылка, которой можно делиться.
Вот, сделал ссылку https://rextester.com/PBR14899
Вот ссылка на мой прошлый код https://rextester.com/SZJFFA94281
Только первое слово выводит, потому что операция >> считывает до пробела. Поэтому вместо всей строки в S попадает слово, а в arr_chars второе слово.
Чтобы считать строку с пробелами, надо использовать или cin.getline, или getline(cin,S) (второе предпочтительнее).
Какие ещё вопросы?
Никаких, спасибо вам огромное!!!
Хотя все же есть, не могу понять куда именно мне вставить getline(cin,S)
Огромное вам спасибо!!!