Тяжело бывает привыкать к строкам в стиле C начинающим программистам. Одна простая символьная переменная в C++ умеет доставлять хлопот, а тут целый массив строк.
В старом стиле строка задавалась как массив символов. В современном стиле строка задаётся, как объект класса string библиотеки STL. В большей части статьи речь будет идти о старом стиле. Хотя работать с любой строкой можно как с обычным массивом символов.
Чтобы задать массив строк, надо массив символов завернуть в массив-оболочку. Вот это вам и необходимо понять. Заворачивание одномерного массива в массив даёт на выхлоп двумерный массив. Заворачивание двумерного массива в массив даёт трёхмерный массив и т. д. Нам сейчас нужно только одно заворачивание: массив символов заворачивается в массив. Наипростейший вариант работы с массивом строк в старом стиле выглядит так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//Borland C++ 3.1 Массив строк Листинг #1
#include <iostream.h>
#include <conio.h>
intmain(){
clrscr();
cout<<"input strings:\n";
chararr_stings[4][255];//4 строки, каждая может вместить
//255 символов
cin.getline(arr_stings[0],255);//чтение строк с клавиатуры
cin.getline(arr_stings[1],255);
cin.getline(arr_stings[2],255);
cin.getline(arr_stings[3],255);
cout<<"\nOurs strings:\n";
for(autoi=0;i<20;i++)cout<<arr_stings[i]<<'\n';//вывод строк на экран
cin.get();
}
Листинг #1 написан не очень здорово, небольшие исправления этого кода показаны в листинге #2, но пояснять удобно по первому. Массив строк это ничто иное, как массив массивов символов. В листинге #1 основной массив arr_string состоит из четырёх массивов, способных вместить в себя по 255 символов каждый (254 символа и признак конца строки). Вот и всё, что нужно научиться понимать.
Задавать количество элементов массивам непосредственно в исходном коде лучше с помощью специального квалификатора неизменяемости: const. Так в случае нужды изменения числа не придётся лопатить весь код, а достаточно будет производить изменение в одном месте. Для ясности: вы используете подобие листинга #1, вам надо изменить число 255, например, на 30, чтобы сократить потребляемую программой память, вместо того, чтобы изменять 255 по всему коду руками, можно сделать так, чтобы изменение происходило только один раз, а делается это с помощью переменной с квалификатором const. Этот случай относится к ситуации, когда количество задаётся прямо в пишемом вами коде. Если задавать количество клавиатурой, то работа с кодом ведётся в другом ключе, про другой ключ будет написано после. Сейчас небольшая модификация первого кода.
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
//Borland C++ 3.1 Массив строк Листинг #2
#include <iostream.h>
#include <conio.h>
intmain(){
clrscr();
constintN=255;//Количество символов для строки
constintrow=4;//Количество строк
inti;//счётчик для цикла
cout<<"input "<<row<<" strings:\n";//диалог программы с пользователем
chararr_stings[row][N];//Массив строк,
//Массив хранит row строк,
//каждая строка вмещает N символов
//если считать признак конца строки,
//то N-1 символов + символ-признак
//Чтение строк с клавиатуры с помощью цикла
for(i=0;i<row;i++){
cout<<i+1<<". ";//диалог программы с пользователем
cin.getline(arr_stings[i],N);//чтение строки с клавиатуры
}
//Вывод информации на экран
cout<<"\nOurs strings:\n";
for(i=0;i<row;i++)cout<<arr_stings[i]<<'\n';
cin.get();
}
Листинг #2 по сути тот же #1, но в немного лучшем стиле. Теперь не нужно бегать по всему коду, чтобы изменять количества. Основа всё та же: в массиве arr_strings хранится row символьных массивов, каждый из которых умеет вмещать в себя N элементов. В код был добавлен цикл-for и немного изменено диалоговое оформление.
Это, собственно, весь принцип организации массива строк. При необходимости задавать количество элементов вручную, требуются дополнительные знания. Такие манёвры делаются с помощью указателей. До этой статьи указатели не описывались, поэтому совсем новичку придётся разбираться с неописываемой сейчас информацией. Тема указателей выходит за рамки этой статьи, поэтому прошу меня понять, статья сильно усложнится, если отвлечься от основной темы. Тем не менее, пример с указателями сейчас будет приведён.
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
//Borland C++ 3.1 Массив строк Листинг #3
#include <iostream.h>
#include <conio.h>
intmain(){
clrscr();
intN=0;//количество символов в строке
introw=0;//количество строк
inti=0,j=0;//счётчики
cout<<"input row: ";
cin>>row;//ввод количества строк
cout<<"input N: ";
cin>>N;//ввод числа символов в строке
cin.ignore();//чтобы не проглотило символ '\n';
cout<<"input "<<row<<" strings:\n";//информация по вводу для пользователя
//Выделение памяти
char**arr_stings=newchar*[row];
for(i=0;i<row;i++){
arr_stings[i]=newchar[N];
}
//Конец выделения памяти
for(i=0;i<row;i++){
cout<<i+1<<". ";
cin.getline(arr_stings[i],N);
}
cout<<"\nOurs strings:\n";
for(i=0;i<row;i++)cout<<arr_stings[i]<<'\n';
cin.get();
//Очистка памяти
for(i=0;i<row;i++){
delete[]arr_stings[i];
}
delete[]arr_stings;
//Конец очистки памяти
}
Листинг #3 новичку может оказаться сложным к пониманию. Так уж повелось в C++, что для массивов задаётся неизменяемое число элементов. Даже при задании количества с помощью ручного выделения памяти, память выделяется единожды и живёт, пока массив живёт. В случае использования указателей существует возможность уничтожить массив непосредственно в ходе работы программы с целью создания нового массива, таким образом можно имитировать работу изменения выделенной памяти.
Корректировка вмещаемого числа элементов в массиве требует создания нового массива на основании корректируемого, при этом корректируемый массив по завершению создания нового на основе себя — должен быть удалён.
Так уж работает С++. Вы это обязательно поймёте. Всё, что было сейчас показано, относится к низкоуровневому программированию, это скорее С, а не С++. Современный С++ за многие годы своего существования успел претерпеть много изменений, да и сам по себе во многом от С отличался. В современных компиляторах для работы со строками используют тип string из библиотеки STL, а в качестве массива, заменяющего обычный массив стиля С, используют vector из той же библиотеки STL.
Когда вы пишете в своём коде "using namespace std", то используете пространство имён стандартной библиотеки, т. е. пространство имён STL.
Библиотечные типы умеют самостоятельно разбираться с памятью, к тому же хорошо оптимизированы для работы. Если вас не ограничивают в выборе, то используйте библиотеку STL. Хотя, конечно, то, что было показано выше, несомненно полезно для саморазвития. В листинге #4 будет показан код создания массива строк: vector<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
//clang Массив строк Листинг #4
#include <iostream>
#include <string> //для string
#include <vector> //для vector
#include <algorithm>
usingnamespacestd;
intmain(){
vector<string>v;//вектор строк
stringS;
auto count=0u;
cout<<"Input strin's count: ";
cin>>count;//Считывание количества строк в переменную с клавиатуры
cin.ignore();//чтобы не глотало '\n'
for(autoi=0u;i<count;i++){
cout<<"input "<<i+1<<" string: ";
getline(cin,S);
v.emplace_back(S);//v.push_back(S)
}
//Вывод данных
cout<<"\nour strings:\n";
for(constauto&i:v) cout << i << '\n';
}
Листинг #4 достаточно мал, оттого понимание его ничем не затруднено. Единственное, что может заставить почесать вас ваши затылки, это суффикс "u" около чисел. Это такой способ обозначить беззнаковое целое, т. е. unsigned. Этот тип данных как-то логичнее использовать при обходах массивов, ведь в массиве не может быть отрицательных индексов: начало ноль и только вперёд. Поскольку тип объявлен как auto, то компилятор берёт на себя ответственность за выбор подходящего типа, а чтобы наверняка было беззнаковое, мной добавляется суффикс к числу.
Расскажите про все способы создания массивов строк:
1. Явно:
C++
1
charArray[10][10];
В скобках нельзя ставить переменные (иначе компилятор не поймет сколько памяти под него выделять), поэтому и массив, и сами строки получаются фиксированной длины.
Вариант мало подходящий для строк, но в некоторых случаях (например, при переборе базы маркировок, имеющих строго определенную длину).
2. Массив указателей:
C++
1
2
3
4
5
char*Array[x];
for(inti=0;i<x;i++)
{
Array[i]=newchar[]//или new char[y];
}
x — количество строк, y — длинна строки. Причем "y" могут отличаться у строк в одном массиве.
3. Указатель на массив указателей:
C++
1
2
3
4
5
6
char**Array;
Array=newchar*[x];
for(inti=0;i<x;i++)
{
Array[i]=new*char;
}
Почти то же самое, что и в предыдущем варианте.
4. Встроенный тип string:
string MASSIV_STROK[3]={"строка 1","строка 2","строка 3"};
Это вы сами себя спросили и сами себе ответили вслух?) или это просьба такая. Я не совсем понял. Все я описать не смогу, способов немало и я знаю не все (из тех, которые вами не указаны, знаю не все).
и в Borland C++ 3.1 нету встроенного типа string, (это я к тому, что некоторых насильно принуждают использовать старьё, которое, например, я использовал для выдачи материалов на сайте).
Это небольшая просьба о дополнении статьи или создании продолжения…))
Кстати, было бы неплохо, если бы на вашем ресурсе появился раздел, где новички могли бы оставлять заявки с просьбами по созданию статей на интересующие их темы в С++ (а может даже устроить какое-нибудь голосование по выпуску следующей статьи 🙄 ) 😀
У меня сейчас время уходит не на С++, поэтому даже если и будет такой раздел, то появляться статьи по просьбам начнут далеко не сразу. Можно, конечно, сразу и сразу на лишь бы отвалили, но я так не желаю.
Вашу просьбу попробую выполнить, но как и сказал, появится это не сразу. Мне нужно два фактора: «желание, свободное время». Это будет не вам полезно, а тем кто придет после вас. На 99% уверен, что вот эту вашу просьбу выполню.
Да и когда кто-то просил описать что-то в комментариях под любой темой, то если я мог, то не отказывал. Вы ведь немало страниц этого сайта прочли и, наверное, видели редкие просьбы.
Sergio, просьба об описании варианта 2 мне понятна. Хочется максимально эффективно экономить память и не забивать ее пустотой, но за кажущейся эффективностью от экономии памяти прячется вполне реальная тормознутость.
Если вы так поставите вопрос, как в варианте 2, то 99% вы получите ответ: «используйте вектор, используйте string». Дело в том, что на этапе создания строки нужно выделять под символы память, а чтобы не выделять лишнего, нужно выделять её под каждый символ. Чтобы выделять ее под каждый символ, сохраняя уже записанное (довыделить), нужно сохранять данные, очищать память, перевыделять память и записывать туда сохраненные данные. Это будет выполняться для каждого нового введенного символа. Было бы всё просто, но операции выделения памяти и очистки памяти дорогостоящие, это вы можете почитать где угодно. Следовательно, лучше пореже выделять и очищать. А для строк, по 255,500,1000 символов, да еще и если все они в массив записываются неэффективность должна стать заметна достаточно быстро. только на выделениях, очистке и копировании будет теряться уйма времени.
Я не уверен, что вначале такими вещами полезно забивать голову новичкам. Хотя способности всех различны, но большинство эту тему трудно поймут, как бы я объяснить не старался.
❗ В самом первом посте я описал способы создания массивов строк, с которыми столкнулся на разных источниках в процессе своего обучения. Об их плюсах и минусах мне пока ещё сложно говорить, как новичку… Просто посчитал нужным немного дополнить статью, тем самым помогая совершенствовать Ваш столь полезный ресурс, который меня сразу заинтересовал. 😉
➡ Про недостатки выделения и очистки динамической памяти уже в курсе, поэтому предпочтительно использовать автоматическую память (как знаем, всего 3 вида памяти: статическая, динамическая и автоматическая) для создания массивов строк. В своём проекте воспользовался следующим:
Попробуйте теперь использовать ваш вариант из проекта, но при этом вводить значения не программно, а с клавиатуры. Вы уже немного знакомы с ожидаемым мною эффектом.
И ваш вариант — это не тот вариант, о котором вы просили. Фиксированное число символов в строке. Но, как, я сказал, с фиксированным делать лучше, чем под каждую строку отводить индивидуальную память.
Это особенность реализации циклов внутри компиляторов. В старых компиляторах переменная объявленная внутри цикла продолжала жить до конца работы функции (программы). В современных, такая переменная живет только внутри цикла, поэтому надо или объявлять ее до циклов всего 1 раз или же внутри каждого цикла для каждой итерации.
Т.к. я использовал старый компилятор, то у меня так как и должно там быть (но лучше, конечно, старые не использовать).
Ну,допустим,пользователь вводит размер строки, а программа должна делить текст(который уже есть в компьютере) вызвать и разделить этот текст на строки (размер) .
Само задание:
Разбиение на строки. Входные данные: текстовый файл на естественном
английском языке, а также длина строки. Выходные данные: текстовый файл, в котором
текст из исходного файла разбит на строки длиной не превышающие заданного
количества символов. Разрывать целые слова на части не разрешается.
А у Вас что-нибудь есть или Вы хотите, чтобы я за Вас делал Ваше задание?
Я могу подсказать. За слово нужно принимать как само слово, так и разделитель между словами. Это как 2 вида слова. Заполнить массив словами из текста. Сделать цикл, в котором будет условие до тех пор, пока общее число символов + число символов следующего слова не превысит выбранный размер и выводить в цикле символы.
Должен получиться цикл в цикле. Внешний должен закончится с последним словом. На каждой итерации внешнего добавлять перенос строки.
Могут быть нестыковки. Например длина слова превышает выбранный размер строки.
Расскажите про все способы создания массивов строк:
1. Явно:
В скобках нельзя ставить переменные (иначе компилятор не поймет сколько памяти под него выделять), поэтому и массив, и сами строки получаются фиксированной длины.
Вариант мало подходящий для строк, но в некоторых случаях (например, при переборе базы маркировок, имеющих строго определенную длину).
2. Массив указателей:
x — количество строк, y — длинна строки. Причем "y" могут отличаться у строк в одном массиве.
3. Указатель на массив указателей:
Почти то же самое, что и в предыдущем варианте.
4. Встроенный тип string:
string MASSIV_STROK[3]={"строка 1","строка 2","строка 3"};
Это вы сами себя спросили и сами себе ответили вслух?) или это просьба такая. Я не совсем понял. Все я описать не смогу, способов немало и я знаю не все (из тех, которые вами не указаны, знаю не все).
и в Borland C++ 3.1 нету встроенного типа string, (это я к тому, что некоторых насильно принуждают использовать старьё, которое, например, я использовал для выдачи материалов на сайте).
Это небольшая просьба о дополнении статьи или создании продолжения…))
Кстати, было бы неплохо, если бы на вашем ресурсе появился раздел, где новички могли бы оставлять заявки с просьбами по созданию статей на интересующие их темы в С++ (а может даже устроить какое-нибудь голосование по выпуску следующей статьи 🙄 ) 😀
У меня сейчас время уходит не на С++, поэтому даже если и будет такой раздел, то появляться статьи по просьбам начнут далеко не сразу. Можно, конечно, сразу и сразу на лишь бы отвалили, но я так не желаю.
Вашу просьбу попробую выполнить, но как и сказал, появится это не сразу. Мне нужно два фактора: «желание, свободное время». Это будет не вам полезно, а тем кто придет после вас. На 99% уверен, что вот эту вашу просьбу выполню.
Да и когда кто-то просил описать что-то в комментариях под любой темой, то если я мог, то не отказывал. Вы ведь немало страниц этого сайта прочли и, наверное, видели редкие просьбы.
Sergio, просьба об описании варианта 2 мне понятна. Хочется максимально эффективно экономить память и не забивать ее пустотой, но за кажущейся эффективностью от экономии памяти прячется вполне реальная тормознутость.
Если вы так поставите вопрос, как в варианте 2, то 99% вы получите ответ: «используйте вектор, используйте string». Дело в том, что на этапе создания строки нужно выделять под символы память, а чтобы не выделять лишнего, нужно выделять её под каждый символ. Чтобы выделять ее под каждый символ, сохраняя уже записанное (довыделить), нужно сохранять данные, очищать память, перевыделять память и записывать туда сохраненные данные. Это будет выполняться для каждого нового введенного символа. Было бы всё просто, но операции выделения памяти и очистки памяти дорогостоящие, это вы можете почитать где угодно. Следовательно, лучше пореже выделять и очищать. А для строк, по 255,500,1000 символов, да еще и если все они в массив записываются неэффективность должна стать заметна достаточно быстро. только на выделениях, очистке и копировании будет теряться уйма времени.
Я не уверен, что вначале такими вещами полезно забивать голову новичкам. Хотя способности всех различны, но большинство эту тему трудно поймут, как бы я объяснить не старался.
❗ В самом первом посте я описал способы создания массивов строк, с которыми столкнулся на разных источниках в процессе своего обучения. Об их плюсах и минусах мне пока ещё сложно говорить, как новичку… Просто посчитал нужным немного дополнить статью, тем самым помогая совершенствовать Ваш столь полезный ресурс, который меня сразу заинтересовал. 😉
➡ Про недостатки выделения и очистки динамической памяти уже в курсе, поэтому предпочтительно использовать автоматическую память (как знаем, всего 3 вида памяти: статическая, динамическая и автоматическая) для создания массивов строк. В своём проекте воспользовался следующим:
💡 По сути похоже на приведённый мной 4й вариант, но без использования string. Обратиться к нужной строке в массиве очень просто:
Попробуйте теперь использовать ваш вариант из проекта, но при этом вводить значения не программно, а с клавиатуры. Вы уже немного знакомы с ожидаемым мною эффектом.
И ваш вариант — это не тот вариант, о котором вы просили. Фиксированное число символов в строке. Но, как, я сказал, с фиксированным делать лучше, чем под каждую строку отводить индивидуальную память.
В строках 12,15,20 в первом коде должно быть
;А в целом весьма доходчиво.Спасибо.
Это особенность реализации циклов внутри компиляторов. В старых компиляторах переменная объявленная внутри цикла продолжала жить до конца работы функции (программы). В современных, такая переменная живет только внутри цикла, поэтому надо или объявлять ее до циклов всего 1 раз или же внутри каждого цикла для каждой итерации.
Т.к. я использовал старый компилятор, то у меня так как и должно там быть (но лучше, конечно, старые не использовать).
А как разбить текст на строки,размер которых вводится пользователем,не деля целые слова?(В C++)
Пример разбития нужен. И что означает не деля целые слова не понятно.
Ну,допустим,пользователь вводит размер строки, а программа должна делить текст(который уже есть в компьютере) вызвать и разделить этот текст на строки (размер) .
Само задание:
Разбиение на строки. Входные данные: текстовый файл на естественном
английском языке, а также длина строки. Выходные данные: текстовый файл, в котором
текст из исходного файла разбит на строки длиной не превышающие заданного
количества символов. Разрывать целые слова на части не разрешается.
А у Вас что-нибудь есть или Вы хотите, чтобы я за Вас делал Ваше задание?
Я могу подсказать. За слово нужно принимать как само слово, так и разделитель между словами. Это как 2 вида слова. Заполнить массив словами из текста. Сделать цикл, в котором будет условие
до тех пор, пока общее число символов + число символов следующего слова не превысит выбранный размер
и выводить в цикле символы.Должен получиться цикл в цикле. Внешний должен закончится с последним словом. На каждой итерации внешнего добавлять перенос строки.
Могут быть нестыковки. Например длина слова превышает выбранный размер строки.
Спасибо.
Borland C++ ver 3.1 — не работает.
Ребят помогите пожалуйста!
У меня есть несколько слов, максимум 15.
Первое слово известно и оно будет первым без изменений, мне надо сделать массив слов, где другие будут подставляться и создавать новую цепочку слов.
Например: 1 слово-кот…константа
2 слово-слон, бабочка, флаг
3 слово-яхта, мяч, зеркало, песок и т.д.
Надо чтобы 1 слово присоединялось к другим вариантам.
кот-слон-яхта….
кот-слон-мяч
кот-слон-зеркало и т.д.
в ручную уже голова пухнет писать(
уебан полуприпизденный.. ты б еще не фене рассказывал.. «выхлоп», «заворачивание» ..нормальных словей забыл уже?