В C++ работа со строками может происходить различными способами. В первых версиях C++ работа со строками происходила также, как в языке C. Со временем язык C++ развивается, следствием развития оказываются новые (относительно времени) возможности. Одной из возможностей, появившихся не сразу, становится обработка строк типа string.
В старых версиях компиляторов нет поддержки типа string
В старых компиляторах, которые писались тогда, когда ещё не было никакой стандартизации, не было поддержки языком С++ библиотеки STL, а вместо строкового типа string использовали или указатель на массив символов, или простой массив символов. Строка по сути своей — массив символов. Только в отличие от обычного массива, строка, представляемая как массив, должна иметь признак, обозначающий завершение строки. Строки, представляемые как массив символов, или обрабатываемые с помощью указателя на массив символов, называются Си-строками, строками в стиле Си.
Об этих строках, пришедших в С++ из языка С, в этой статье и будет идти речь. В тексте этой статьи иногда будет написано слово 'Си', это чтобы было яснее призношение, а то 'C' возможно произносить и как 'Ц', и как 'С', и как 'Си', возможно, как-то ещё.
Строку легко представлять в виде массива символов.
Но программист С++ должен подсказывать компилятору, в каком месте такая си-строка завершается:
C++
1
2
3
4
5
6
7
//Borland C++ 3.1 Строки Листинг #1
#include <iostream.h>
intmain(){
charS[255]={'H','E','L','L','O',0};
cout<<S<<'\n';
cin.get();
}
В листинге #1 показано как работать со строкой, представляя строку в виде массива символов. Используется самый обычный массив, но в массиве обязательно должен быть завершающий ноль. Этот ноль сам по себе число и является признакм того, что строка закончена. Поскольку сам массив может быть достаточно большим, а строка только малой его частью, должен быть какой-то разграничитель, помогающий компилятору выделять только нужную часть массива. Этим разграничителем и является число ноль, не спутайте с символом. Поскольку массив состоит из символов, то все отдельные символы при инициализации нужно оборачивать в одинарные кавычки.
C++
1
2
3
4
5
6
7
8
//Borland C++ 3.1 Строки Листинг #2
#include <iostream.h>
intmain(){
charS[255]={'H','E','L','L','O',0};
cout<<S<<'\n';
S[0]='K';//Работать так же как с массивом
cin.get();
}
Работа с массивом символов ничем не отличается от работы с обычным массивом. Массив, он и есть массив. Единственное, что нужно понимать: разграничение массива при работе с ним как со строкой. Если вы вылезаете за пределы строки, то там всё равно нужного ничего нет, поэтому обрабатывать ту часть бывает бессмысленно.
Массивы символов, которые представляют из себя непосредственно строки, не всегда нужны. Иногда необходим обычный массив символов, который компилятором не будет восприниматься как строка. В таком случае, конечно, разграничителя не требуется, а работа происходит абсолютно так же, как с любым массивом.
Если вам нужно обрабатывать или отдельные слова, или предложение, или любое строковое представление текста, то как один из вариантов — массив символов, который внутри себя содержит разграничитель, являющийся признаком конца строки.
Инициализировать (задавать начальные значения) строке показанным в листинге #1 способом неудобно, правила языка С++ разрешают использовать для инициализации двойные кавычки. Компилятор сам разложит символы в ячейки массива и добавит признак конца строки, если эти двойные кавычки используются при инициализации:
C++
1
2
3
4
5
6
7
//Borland C++ 3.1 Строки Листинг #3
#include <iostream.h>
intmain(){
charS[255]="HELLO";
cout<<S<<'\n';
cin.get();
}
Листинги #1 и #3 в конечном итоге компилятором видятся одинаково, но нам удобнее использовать листинг #3. В обоих случаях строка представляется нами компилятору как массив символов, компилятор и работает со строкой как с массивом. Только, в случае использования нами двойных кавычек в инициализации — компилятору забот немного больше. Вот и всё различие.
Третий вариант работы со строками в стиле языка С, способный приводить юных программистов в некоторое замешательство, — использование указателя для работы со строками:
C++
1
2
3
4
5
6
7
8
//Borland C++ 3.1 Строки Листинг #4
#include <iostream>
usingnamespacestd;
intmain(){
char*S="HELLO";//Указатель на символьный массив
cout<<S<<'\n';
}
Указатели вообще сложно объяснить новичкам. Неграмотное использование указателей делает коды очень сложночитаемыми и трудноотлаживаемыми, т. е. в таких трудно исправлять ошибки. Но строки, как полноценные объекты, обрабатывать сложнее, чем обрабатывать обычные переменные. Обычная переменная — она одна и сама по себе, а в строке таких "сама по себе" гарем настоящий. Ведь, чтобы обрабатывать массив, компилятор так или иначе, обращается к каждому элементу массива, а если элементов много, при этом требуется большое число повторений, то обработка каждой отдельной ячейки массива оказывает очень заметный эффект тормознутости: программа работает долго. Указатели при правильном к себе подходе и верном использовании помогают избежать ненужных издержек. Поэтому использование указателей в языке С и С++ неизбежно. Современные программисты С++ часто будут отговаривать вас использовать указатели для работы со строками, но иметь понятие о работе со строками с помощью указателей в любом случае нужно.
Листинг #4 достаточно проблематичен для новичков. Эта его проблематичность связана с тем, что в начале изучения языков программирования у изучающих могут возникать сложности с осмыслением понятия константности: непонятно, что это, непонятно, зачем оно, и т. п. Вдобавок к этой возникающей неясности добавляется и некоторое самовольство языка С++: что-то где-то дописывать или что-то где-то откидывать. Эти факторы сильно влияют на понимание кода, показываемого в листинге #4.
В листинге #4 переменная S не является строкой, а это очень важно для понимания самого примера. S — это указатель, указывающей на массив символов. Массив символов легко интерпретируется как строка, поэтому можно говорить, что на строку. Сама строка "HELLO", написанная в исходном коде программы, в данном случае неизменяемая строка. "HELLO" в нашем случае ни в какую переменную не сохраняется, а просто хранится какой-то момент времени в памяти. Переменная S указывает на начало этого участка памяти. А компилятор С++ этот участок памяти объявляет "нерушимым Советским Союзом", т. е. не даёт ничего изменять внутри. Поэтому при внешнем отсутствии проблемы сложности у начинающих возникают достаточно быстро:
C++
1
2
3
4
5
6
7
8
9
10
//Borland C++ 3.1 Строки Листинг #5
#include <iostream.h>
intmain(){
char*S="HELLO";
S[0]='1';//А изменять нельзя!
cout<<S<<'\n';
cin.get();
}
Поскольку нельзя изменять значение, нельзя использовать и клавиатуру для сохранения значений в такой, так сказать, формации.
На самом деле компилятор дописывает модификатор неизменяемости, что новичкам, разумеется, не очевидно. Листинг #5 становится вот таким:
C++
1
2
3
4
5
6
7
8
9
10
//Borland C++ 3.1 Строки Листинг #6
#include <iostream.h>
intmain(){
constchar*S="HELLO";
S[0]='1';//А изменять нельзя, потому что const char!
cout<<S<<'\n';
cin.get();
}
Этот момент осознавать сложновато, но ничего не поделать. Такой нюанс работы со строками в языке С++ имеет место быть. Другое дело, если взять управление указателем в свои руки, самостоятельно выделять и чистить память, тогда компилятор С++ не выёживается. В старых компиляторах нельзя было написать нижепоказываемый код. Если компилятор не поддерживает С++11, то показываемый ниже код не будет ему понятен.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
//clang Строки Листинг #6
#include <iostream>
usingnamespacestd;
intmain(){
char*S=newchar[255]{"HELLO"};
S[0]='1';
cout<<S<<'\n';
cin.get();
delete[]S;
}
Листинг #6 только следствие развития языка, долгое время этот код не считался бы правильным, но после принятия стандарта в 2011г. такое в С++ разрешается.
В С++ любая обработка строк в стиле С, оказывается обычной обработкой массива символов. Но в отличие от обычных массивов, при работе со строками используется разграничитель: признак конца строки.
Cама строка, заключаемая в двойные кавычки во время инициализации, неожиданно для начинающих, может быть воспринята компилятором как неизменяемая строка.
Чтобы работать с такими Си-строками, нужно научиться понимать, когда строка считается неизменяемой. За исключением показанного случая, не могу вспомнить, чтобы было подобное самовольство (полностью оправданное правилами языка) со стороны компилятора С++.
Если вас ничто не ограничивает в использовании строк, то предпочитайте Си-строкам строки из библиотеки STL, т. е. строкам string. Тип string появился в С++ после принятия правил языка в 2003г. В некоторые года правила языка совершенствуются, принимаются на обсуждение коллективом разработчиков, после чего какие-то из них утверждаются и фиксируются в документе, называемом «стандарт С++». Вот в 2003г. и произошли изменения, которыми в язык С++ была включена библиотека STL, а в библиотеке STL описан класс string. Т. е. компиляторы С++, написанные в 2003г и позднее, обязательно включают в себя этот string. Чтобы этот тип использовать, обязательно нужно подключать заголовочный файл string:
C++
1
2
3
4
5
6
7
8
9
10
//clang Строки Листинг #7
#include <iostream>
#include <string> //для строк
usingnamespacestd;
intmain(){
stringS="HELLO";
cout<<S<<'\n';
}
Некоторые компиляторы могут не требовать обязательного подключения этого заголовочного файла. Но чтобы гарантировано быть уверенными, обязательно его подключать своими силами. Некоторые новички задаются вопросами, отчего не работает string, а ошибка столь банальна: забытый подключенный заголовочный файл. Надеюсь, что разницу с версиями компиляторов объяснять не нужно: на многое влияет время.
Как объявлять и инициализировать строковые переменные я показал. Осталось показать, как работать с такими переменными посредством клавиатуры. А то как-то очень неполно всё получится.
Работа с Си-строками и работа со строками string очень во многом схожа, но имеется немало отличий.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Borland C++ 3.1 Считывание строки с клавиатуры Листинг #8
#include <iostream.h>
#include <conio.h>
intmain(){
clrscr();
charS[255]="";
cin.getline(S,255);
cout<<S<<'\n';
cin.get();
return0;
}
//Нужно ввести строку и нажать Enter
C++
1
2
3
4
5
6
7
8
9
10
11
12
//clang Считывание строки с клавиатуры Листинг #9
#include <iostream>
#include <string>
usingnamespacestd;
intmain(){
stringS;
getline(cin,S);
cout<<S<<'\n';
}
В листингах #8 и #9 вы можете видеть первое же отличие: одним из способов чтения строки в стиле С, является способ с указанием имени переменной, являющейся строкой, и нужной для неё длины; а при работе со string нужно указывать объект cin, обозначающий клавиатуру и имя переменной.
Несмотря на то, что в обоих случаях написано getline, вся работа этих getline различна, можете считать их разными функциями, потому что они и есть разные функции.
Одной из доставучих проблем для новичков, изучающих С++, оказывается чтение строк до первого пробела. Привыкая считывать с клавиатуры значения обычных численных переменных, новички первое время используют тот же способ для сохранения строк в переменные, что в конечном итоге оказывается ложным путём: начинают появляться вопросы о строках.
C++
1
2
charS[255];
cin>>S;//Значение в строку предполагается записать с клавиатуры.
Этот кусочек кода может быть написан по наитию, что, мол, переменная, а значит, сохранять в переменную надо так. До тех пор, пока в строках нет пробелов, такой код успешно работает, из-за чего ошибка бывает замечена не сразу. Операция >> напрямую влияет на чтение строки до первого пробела. Само описание процесса выходит за рамки статьи, просто показываю код, который может поспособствовать пониманию:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//clang Листинг #10
#include <iostream>
usingnamespacestd;
classMyClass{
public:
voidoperator>>(constintx){//Так определяется наше поведение операции >>
cout<<"HA-HA";
}
};
intmain(){
MyClassx;
x>>10;
}
В листинге #10 показан пример для пользовательского класса. Похожий процесс происходит с объектом cin. И просто создателями языка написано там таким образом, чтобы первый пробел считался разграничителем. Тому есть причины, но всё это, как уже было написано, выходит за рамки статьи.
Поэтому при работе с полноценными строками используются функции, даже для чтения с клавиатуры используется функция, а не операция >>. Хотя, если нужно, например, только слово, то операцию >> использовать никто не запрещает. Т. е. если вы знаете, что строка не будет разбита из-за наличия пробелов, то ваше право выбирать любой вариант.
Для работы со строками чаще всего используют различные обрабатывающие функции.
Наиболее важными для новичков в работе с Си-строками могут оказаться встроенные в язык функции — вычисления длины строки: strlen(), объединения двух строк: strcat(), поиска символа в строке: strchr(), поиска подстроки в строке: strstr(). Чтобы использовать эти функции, необходимо подключать заголовочный файл string.h.
В любом случае вы должны научиться различать работу в Си-стиле и работу в С++ стиле. Си-стиль работы со строками — это когда строки представляются компилятору как массивы или используется указатель на массив символов, а С++ — стиль — это когда используются объекты класса string.
Работать со строками иногда довольно сложно: выбирать корни, приставки, суффиксы или окончания слов, правильно склонять ВСЕ фамилии, генерировать интересные стихи… Хватает задач, в которых влияние истории в совокупности с человеческим мышлением компьютерам не под силу. Тем не менее компьютеры замечательно справляются с очень большим пластом задач, связанных со строками.
Из-за того, что любая строка легко оказывается массивом символов, к ней можно обращаться как к массиву, т. е. каждый отдельный элемент легко вытаскивать по индексу, а если его разрешено изменять, то, соответственно, и изменять по индексу, как это делается при работе с обычным массивом. Многие новички плохо понимают этот момент. Но такое легко запомнить: любую строку в С++ очень просто интерпретировать как массив символов. Массив строк, в свою очередь, можно представлять как двумерный массив символов.
На этом знакомство со строками я заканчиваю. Надеюсь было интересно и познавательно, что эта статья научила вас понимать начало работы со строками в С++.
Один комментарий на «“Знакомство со строками в С++”»
хуйня , нихуя не понятно