C строки функции обработки строк. Операции со строками. Основные функции стандартной библиотеки string.h

  • 19.04.2019

Хабра, привет!

Не так давно у со мной произошел довольно-таки интересный инцидент, в котором был замешан один из преподавателей одного колледжа информатики.

Разговор о программировании под Linux медленно перешел к тому, что этот человек стал утверждать, что сложность системного программирования на самом деле сильно преувеличена. Что язык Си прост как спичка, собственно как и ядро Linux (с его слов).

У меня был с собой ноутбук с Linux, на котором присутствовал джентльменский набор утилит для разработки на языке Си (gcc, vim, make, valgrind, gdb). Я уже не помню, какую цель мы тогда перед собой поставили, но через пару минут мой оппонент оказался за этим ноутбуком, полностью готовый решать задачу.

И буквально на первых же строках он допустил серьезную ошибку при аллоцировании памяти под… строку.

Char *str = (char *)malloc(sizeof(char) * strlen(buffer));
buffer - стековая переменная, в которую заносились данные с клавиатуры.

Я думаю, определенно найдутся люди, которые спросят: «Разве что-то тут может быть не так?».
Поверьте, может.

А что именно - читайте по катом.

Немного теории - своеобразный ЛикБез.

Если знаете - листайте до следующего хэдера.

Строка в C - это массив символов, который по-хорошему всегда должен заканчиваться "\0" - символом конца строки. Строки на стеке (статичные) объявляются вот так:

Char str[n] = { 0 };
n - размер массива символов, то же, что и длина строки.

Присваивание { 0 } - «зануление» строки (опционально, объявлять можно и без него). Результат такой же, как у выполнения функций memset(str, 0, sizeof(str)) и bzero(str, sizeof(str)). Используется, чтобы в неинициализированных переменных не валялся мусор.

Так же на стеке можно сразу проинициализировать строку:

Char buf = "default buffer text\n";
Помимо этого строку можно объявить указателем и выделить под нее память на куче (heap):

Char *str = malloc(size);
size - количество байт, которые мы выделяем под строку. Такие строки называются динамическими (вследствие того, что нужный размер вычисляется динамически + выделенный размер памяти можно в любой момент увеличить с помощью функции realloc()).

В случае со стековой переменной, для определения размера массива я использовал обозначение n, в случае с переменной на куче - я использовал обозначение size. И это прекрасно отражает истинную суть отличия объявления на стеке от объявление с аллоцированием памяти на куче, ведь n как правило используется тогда, когда говорят о количестве элементов. А size - это уже совсем другая история…

Нам поможет valgrind

В своей предыдущей статье я также упоминал о нем. Valgrind ( , два - небольшой how-to) - очень полезная программа, которая помогает программисту отслеживать утечки памяти и ошибки контекста - как раз те вещи, которые чаще всего всплывают при работе со строками.

Давайте рассмотрим небольшой листинг, в котором реализовано что-то похожее на упомянутую мной программу, и прогоним ее через valgrind:

#include #include #include #define HELLO_STRING "Hello, Habr!\n" void main() { char *str = malloc(sizeof(char) * strlen(HELLO_STRING)); strcpy(str, HELLO_STRING); printf("->\t%s", str); free(str); }
И, собственно, результат работы программы:

$ gcc main.c $ ./a.out -> Hello, Habr!
Пока ничего необычного. А теперь давайте запустим эту программу с valgrind!

$ valgrind --tool=memcheck ./a.out ==3892== Memcheck, a memory error detector ==3892== Copyright (C) 2002-2015, and GNU GPL"d, by Julian Seward et al. ==3892== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info ==3892== Command: ./a.out ==3892== ==3892== Invalid write of size 2 ==3892== at 0x4005B4: main (in /home/indever/prg/C/public/a.out) ==3892== Address 0x520004c is 12 bytes inside a block of size 13 alloc"d ==3892== at 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== by 0x400597: main (in /home/indever/prg/C/public/a.out) ==3892== ==3892== Invalid read of size 1 ==3892== at 0x4C30BC4: strlen (vg_replace_strmem.c:454) ==3892== by 0x4E89AD0: vfprintf (in /usr/lib64/libc-2.24.so) ==3892== by 0x4E90718: printf (in /usr/lib64/libc-2.24.so) ==3892== by 0x4005CF: main (in /home/indever/prg/C/public/a.out) ==3892== Address 0x520004d is 0 bytes after a block of size 13 alloc"d ==3892== at 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==3892== by 0x400597: main (in /home/indever/prg/C/public/a.out) ==3892== -> Hello, Habr! ==3892== ==3892== HEAP SUMMARY: ==3892== in use at exit: 0 bytes in 0 blocks ==3892== total heap usage: 2 allocs, 2 frees, 1,037 bytes allocated ==3892== ==3892== All heap blocks were freed -- no leaks are possible ==3892== ==3892== For counts of detected and suppressed errors, rerun with: -v ==3892== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
==3892== All heap blocks were freed - no leaks are possible - утечек нет, и это радует. Но стоит опустить глаза чуть пониже (хотя, хочу заметить, это лишь итог, основная информация немного в другом месте):

==3892== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
3 ошибки. В 2х контекстах. В такой простой программе. Как!?

Да очень просто. Весь «прикол» в том, что функция strlen не учитывает символ конца строки - "\0". Даже если его явно указать во входящей строке (#define HELLO_STRING «Hello, Habr!\n\0»), он будет проигнорирован.

Чуть выше результата исполнения программы, строки -> Hello, Habr! есть подробный отчет, что и где не понравилось нашему драгоценному valgrind. Предлагаю самостоятельно посмотреть эти строчки и сделать выводы.

Собственно, правильная версия программы будет выглядеть так:

#include #include #include #define HELLO_STRING "Hello, Habr!\n" void main() { char *str = malloc(sizeof(char) * (strlen(HELLO_STRING) + 1)); strcpy(str, HELLO_STRING); printf("->\t%s", str); free(str); }
Пропускаем через valgrind:

$ valgrind --tool=memcheck ./a.out -> Hello, Habr! ==3435== ==3435== HEAP SUMMARY: ==3435== in use at exit: 0 bytes in 0 blocks ==3435== total heap usage: 2 allocs, 2 frees, 1,038 bytes allocated ==3435== ==3435== All heap blocks were freed -- no leaks are possible ==3435== ==3435== For counts of detected and suppressed errors, rerun with: -v ==3435== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Отлично. Ошибок нет, +1 байт выделяемой памяти помог решить проблему.

Что интересно, в большинстве случаев и первая и вторая программа будут работать одинаково, но если память, выделенная под строку, в которую не влез символ окончания, не была занулена, то функция printf(), при выводе такой строки, выведет и весь мусор после этой строки - будет выведено все, пока на пути printf() не встанет символ окончания строки.

Однако, знаете, (strlen(str) + 1) - такое себе решение. Перед нами встают 2 проблемы:

  1. А если нам надо выделить память под формируемую с помощью, например, s(n)printf(..) строку? Аргументы мы не поддерживаем.
  2. Внешний вид. Строка с объявлением переменной выглядит просто ужасно. Некоторые ребята к malloc еще и (char *) умудряются прикручивать, будто под плюсами пишут. В программе где регулярно требуется обрабатывать строки есть смысл найти более изящное решение.
Давайте придумаем такое решение, которое удовлетворит и нас, и valgrind.

snprintf()

int snprintf(char *str, size_t size, const char *format, ...); - функция - расширение sprintf, которая форматирует строку и записывает ее по указателю, переданному в качестве первого аргумента. От sprintf() она отличается тем, что в str не будет записано байт больше, чем указано в size.

Функция имеет одну интересную особенность - она в любом случае возвращает размер формируемой строки (без учета символа конца строки). Если строка пустая, то возвращается 0.

Одна из описанных мною проблем использования strlen связана с функциями sprintf() и snprintf(). Предположим, что нам надо что-то записать в строку str. Конечная строка содержит значения других переменных. Наша запись должна быть примерно такой:

Char * str = /* тут аллоцируем память */; sprintf(str, "Hello, %s\n", "Habr!");
Встает вопрос: как определить, сколько памяти надо выделить под строку str?

Char * str = malloc(sizeof(char) * (strlen(str, "Hello, %s\n", "Habr!") + 1)); - не прокатит. Прототип функции strlen() выглядит так:

#include size_t strlen(const char *s);
const char *s не подразумевает, что передаваемая в s строка может быть строкой формата с переменным количеством аргументов.

Тут нам поможет то полезное свойство функции snprintf(), о котором я говорил выше. Давайте посмотрим на код следующей программы:

#include #include #include void main() { /* Т.к. snprintf() не учитывает символ конца строки, прибавляем его размер к результату */ size_t needed_mem = snprintf(NULL, 0, "Hello, %s!\n", "Habr") + sizeof("\0"); char *str = malloc(needed_mem); snprintf(str, needed_mem, "Hello, %s!\n", "Habr"); printf("->\t%s", str); free(str); }
Запускаем программу в valgrind:

$ valgrind --tool=memcheck ./a.out -> Hello, Habr! ==4132== ==4132== HEAP SUMMARY: ==4132== in use at exit: 0 bytes in 0 blocks ==4132== total heap usage: 2 allocs, 2 frees, 1,041 bytes allocated ==4132== ==4132== All heap blocks were freed -- no leaks are possible ==4132== ==4132== For counts of detected and suppressed errors, rerun with: -v ==4132== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) $
Отлично. Поддержка аргументов у нас есть. Благодаря тому, что мы в качестве второго аргумента в функцию snprintf() передаем ноль, запись по нулевому указателю никогда не приведет к Seagfault. Однако, несмотря на это функция все равно вернет необходимый под строку размер.

Но с другой стороны, нам пришлось завести дополнительную переменную, да и конструкция

Size_t needed_mem = snprintf(NULL, 0, "Hello, %s!\n", "Habr") + sizeof("\0");
выглядит еще хуже, чем в случае с strlen().

Вообще, + sizeof("\0") можно убрать, если в конце строки формата явно указать "\0" (size_t needed_mem = snprintf(NULL, 0, «Hello, %s!\n\0 », «Habr»);), но это возможно отнюдь не всегда (в зависимости от механизма обработки строк мы можем выделить лишний байт).

Надо что-то сделать. Я немного подумал и решил, что сейчас настал час воззвать к мудрости древних. Опишем макрофункцию, которая будет вызывать snprintf() с нулевым указателем в качестве первого аргумента, и нулем, в качестве второго. Да и про конец строки не забудем!

#define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0")
Да, возможно, для кого-то будет новостью, но макросы в си поддерживают переменное количество аргументов, и троеточие говорит препроцессору о том, что указанному аргументу макрофункции (в нашем случае это args) соответствует несколько реальных аргументов.

Проверим наше решение на практике:

#include #include #include #define strsize(args...) snprintf(NULL, 0, args) + sizeof("\0") void main() { char *str = malloc(strsize("Hello, %s\n", "Habr!")); sprintf(str, "Hello, %s\n", "Habr!"); printf("->\t%s", str); free(str); }
Запускаем с valgrund:

$ valgrind --tool=memcheck ./a.out -> Hello, Habr! ==6432== ==6432== HEAP SUMMARY: ==6432== in use at exit: 0 bytes in 0 blocks ==6432== total heap usage: 2 allocs, 2 frees, 1,041 bytes allocated ==6432== ==6432== All heap blocks were freed -- no leaks are possible ==6432== ==6432== For counts of detected and suppressed errors, rerun with: -v ==6432== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Да, ошибок нет. Все корректно. И valgrind доволен, и программист наконец может пойти поспать.

Но, напоследок, скажу еще кое-что. В случае, если нам надо выделить память под какую-либо строку (даже с аргументами) есть уже полностью рабочее готовое решение .

Речь идет о функции asprintf:

#define _GNU_SOURCE /* See feature_test_macros(7) */ #include int asprintf(char **strp, const char *fmt, ...);
В качестве первого аргумента она принимает указатель на строку (**strp) и аллоцирует память по разыменованному указателю.

Наша программа, написанная с использованием asprintf() будет выглядеть так:

#include #include #include void main() { char *str; asprintf(&str, "Hello, %s!\n", "Habr"); printf("->\t%s", str); free(str); }
И, собственно, в valgrind:

$ valgrind --tool=memcheck ./a.out -> Hello, Habr! ==6674== ==6674== HEAP SUMMARY: ==6674== in use at exit: 0 bytes in 0 blocks ==6674== total heap usage: 3 allocs, 3 frees, 1,138 bytes allocated ==6674== ==6674== All heap blocks were freed -- no leaks are possible ==6674== ==6674== For counts of detected and suppressed errors, rerun with: -v ==6674== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Все отлично, но, как видите, памяти всего было выделено больше, да и alloc"ов теперь три, а не два. На слабых встраиваемых системах использование это функции нежелательно.
К тому же, если мы напишем в консоли man asprintf, то увидим:

CONFORMING TO These functions are GNU extensions, not in C or POSIX. They are also available under *BSD. The FreeBSD implementation sets strp to NULL on error.

Отсюда ясно, что данная функция доступна только в исходниках GNU.

Заключение

В заключение я хочу сказать, что работа со строками в C - это очень сложная тема, которая имеет ряд нюансов. Например, для написания «безопасного» кода при динамическом выделении памяти рекомендуется все же использовать функцию calloc() вместо malloc() - calloc забивает выделяемую память нулями. Ну или после выделения памяти использовать функцию memset(). Иначе мусор, который изначально лежал на выделяемом участке памяти, может вызвать вопросы при дебаге, а иногда и при работе со строкой.

Больше половины моих знакомых си-программистов (большинство из них - начинающие), решивших по моей просьбе задачу с выделением памяти под строки, сделали это так, что в конечном итоге это привело к ошибкам контекста. В одном случае - даже к утечке памяти (ну, забыл человек сделать free(str), с кем не бывает). Собственно говоря, это и сподвигло меня на создание сего творения, которое вы только что прочитали.

Я надеюсь, кому-то эта статья будет полезной. К чему я это все городил - никакой язык не бывает прост. Везде есть свои тонкости. И чем больше тонкостей языка вы знаете, тем лучше ваш код.

Я верю, что после прочтения этой статьи ваш код станет чуточку лучше:)
Удачи, Хабр!

Строки в C++

Строка - последовательность (массив) символов. Если в выражении встречается одиночный символ, он должен быть заключен в одинарные кавычки . При использовании в выражениях строка заключается в двойные кавычки. Признаком конца строки является нулевой символ \0 . В C++ строки можно описать с помощью символов (массив элементов типа char ), в котором следует предусмотреть место для хранения признака конца строки.

Например, описание строки из 25 символов должно выглядеть так:

Можно описать и массив строк:

Определен массив из 3 строк по 25 байт в каждой.

Для работы с указателями можно использовать (char * ). Адрес первого символа будет начальным значением указателя.

Рассмотрим пример объявления и вывода строк.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include «stdafx.h»
#include
using namespace std;
int main()
{
setlocale(LC_ALL,«Rus» ) ;
//описываем 3 строки, s3- указатель
char s2[ 20 ] , * s3, s4[ 30 ] ;
cout << «s2=» ; cin >> s2; //ввод строки s2
cout << «s2=» << s2<< endl;
//запись в s3 адреса строки, где хранится s4. Теперь в переменных
//(указателях) s3 и s4 хранится значение одного и того же адреса
s3= s4;
cout << «s3=» ; cin >> s3; //ввод строки s3
//вывод на экран строк s3 и s4, хотя в результате присваивния s3=s4;
//теперь s3 и s4 — это одно и тоже
cout << «s3=» << s3<< endl;
cout << «s4=» << s4<< endl;
system («pause» ) ;
return 0 ;
}

Результат работы программы:

Но следует отметить, что если пользователь введет в одну переменную слова разделенные пробелом, то программа будет работать иначе:

Все дело в том, что функция cin вводит строки до встретившегося пробела. Более универсальной функцией является getline .

cin.getline(char *s, int n);

Предназначена для ввода с клавиатуры строки s с пробелами, в строке не должно быть более n символов. Следовательно, для корректного ввода строк, содержащих пробел, необходимо в нашей программе заменить cin>>s на cin.getline(s, 80) .

Операции над строками

Строку можно обрабатывать как массив символов, используя алгоритмы обработки массивов или с помощью специальных функций обработки строк, некоторые из которых приведены ниже. Для работы с этими строками необходимо подключить библиотеку cstring .

Для преобразования числа в строку можно воспользоваться функцией sprintf из библиотеки stdio.h .

Некоторые функции работы со строками:

Прототип функции Описание функции
size_t strlen(const char *s) вычисляет длину строки s в байтах.
char *strcat(char *dest, const char *scr) присоединяет строку src в конец строки dest, полученная срока возвращается в качестве результата
char *strcpy(char *dest, const char *scr) копирует строку scr в место памяти, на которое указывает dest
char strncat(char *dest, const char *dest, size_t maxlen) присоединяет строку maxlen символов строки src в конец строки dest
char *strncpy(char *dest, const char *scr, size_t maxlen) копирует maxlen символов строки src в место памяти, на которое указывает dest
int ctrcmp(const char *s1, const char *s2) сравнивает две строки в лексикографическом порядке с учетом различия прописных и строчных букв, функция возвращает 0, если строки совпадают, возвращает - 1, если s1 располагается в упорядоченном по алфавиту порядке раньше, чем s2, и 1 - в противоположном случае.
int strncmp(const char *s1, const char *s2, size_t maxlen) сравнивает maxlen символов двух строк в лексикографическом порядке, функция возвращает 0, если строки совпадают, возвращает - 1, если s1 располагается в упорядоченном по алфавиту порядке раньше, чем s2, и 1 - в противоположном случае.
double atof(const char *s) преобразует строку в вещественное число, в случае неудачного преобразования возвращается число 0
long atol(const char *s) преобразует строку в длинное целое число, в случае неудачного преобразования возвращается 0
char *strchr(const char *s, int c); возвращает указатель на первое вхождение символа c в строку, на которую указывает s . Если символ c не найден, возвращается NULL
char *strupr(char *s) преобразует символы строки, на которую указывает s, в символы верхнего регистра, после чего возвращает ее

Тип данных string

Кроме работы со строками, как с массивом символов, в C++ существует специальный тип данных string . Для ввода переменных этого типа можно использовать cin , или специальную функцию getline .

getline(cin, s);

Здесь s - имя вводимой переменной типа string .

При описании переменной этого типа можно сразу присвоить значение этой переменной.

string var(s);

Здесь var - имя переменной, s - строковая константа. В результате этого оператора создается переменная var типа string , и в нее записывается значение строковой константы s . Например,

string v(«Hello»);

Создается строка v , в которую записывается значение Hello .

Доступ к i-му элементу строки s типа string осуществляется стандартным образом s[i] . Над строками типа string определенны следующие операции:

  • присваивания, например s1=s2;
  • объединения строк (s1+=s2 или s1=s1+s2) - добавляет к строке s1 строку s2, результат храниться в строке s1, пример объединения строк:
  • сравнения строк на основе лексикографического порядка: s1=s2, s1!=s2, s1s2, s1<=s2, s1>=s2 - результатом будет логическое значение;

При обработке строк типа string можно использовать следующие функции:

  • s.substr(pos, length) - возвращает подстроку из строки s , начиная с номера pos длинной length символов;
  • s.empty() - возвращает значение true, если строка s пуста, false - в противном случае;
  • s.insert(pos, s1) - вставляет строку s1 в строку s , начиная с позиции pos ;
  • s.remove(pos, length) - удаляет из строки s подстроку length длинной pos символов;
  • s.find(s1, pos) - возвращает номер первого вхождения строки s1 в строку s , поиск начинается с номера pos , параметр pos может отсутствовать, в этом случае поиск идет с начала строки;
  • s.findfirst(s1, pos) - возвращает номер первого вхождения любого символа из строки s1 в строку s , поиск начинается с номера pos , который может отсутствовать.

Русский язык для строк

Думаю вы уже заметили, что при выводе русских букв, в консоли появляются «левые» символы. Для того чтобы избежать этого недоразумения, необходимо воспользоваться сторонней функцией CharToOemA . Подключаем библиотеку windows.h , она нужна для того, чтобы наша функция могла преобразовать строки в другую кодировку. Также, нам понадобиться дополнительный символьный массив. Исходный код программы будет выглядеть вот так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include «stdafx.h»
#include
#include
using namespace std;
int main()
{ setlocale(LC_ALL,«Rus» ) ;
char s[ 255 ] = { » Меня надо преобразовать « } ;
char * pre= new char [ 255 ] ;
CharToOemA(s, pre) ; //преобразовываем
cout << s;
delete pre;
system («pause>>void» ) ;
return 0 ;
}

Способ только что описанный достаточно не удобен. Но существует более простой вариант решения «русской» проблемы. Как видите, в программе используется функция setlocale(), вместо этого удобнее вписать в главную функцию следующую конструкцию.

п»їTrustworthy SEO Agency India Can Increase Revenues of Small Businesses

80% users search on Google and other search engines before making a purchase and more than 50% inquiries generated through search engines get converted. These two statistics prove the importance of Search Engine Optimization. There are many as such stats and facts that make a clear point: any small, mid or large scaled business need professional SEO services. Small businesses and startups often face budget issues. They can take help of any trustworthy SEO agency from India to get the best SEO service in their budget to increase their revenues.
Search holds a great impact on consumers’ minds. According to the various statistics shared by major search engine optimization experts on various authorized websites such as Search Engine Land, Moz, SEO Journal, Digital Marketers India, Hubspot, etc. SEO captures a majority of the leads. Also, the leads coming from the organic search results have a higher conversion rate. These stats and consumer behavior make a clearer point that best SEO service is not a luxury, but a necessity for any business.
To bypass the competition and to increase business growth each organization needs to use the Search Engine Optimization services. The big brands can invest enough money for the expert SEO service offered by a top SEO company or an SEO specialist, but small business owners often compromise on the quality of this service due to less budget. It’s a hard fact the small business and startups end up leaving the opportunities that can be created with the professional SEO service or use a cheap SEO service which yields no positive results.
The small business owners and startups can take benefit of professional SEO services even in the limited budget. The best solution is finding a trustworthy SEO company based out of India. In India, there are many SEO experts who are working with the digital marketing agency and offer best-in-the-industry services. They can provide you the required SEO services in your budget. The wages can be negotiated with an SEO agency India to get better services at lower rates. However, don’t fall for cheap SEO service that charges less and promise to give more as expertise comes at its own cost. You must see the portfolio or ask proper questions before contracting a company for your business.
The SEO experts in India are skilled with the best practices of search engine optimization. Also, there are some SEO specialists in India such as Ash Vyas, who specialize in creating the best search engine optimization strategy for a business in stated budget. The SEO professionals will create a clear plan and will also share what can be the expected results. This way you can be well aware of your investment and returns. This helps in making a better business decision.
A good idea is to find and contract a trustworthy SEO company from India that offers the best SEO services as soonest as possible. You may also start with a small budget and limited activities to start getting your WebPages indexed and boosting your keywords in search engines. Don’t wait for the perfect time or a day when you will have thousands of dollars to invest in the best SEO services. Starting early will help you get quicker results when you can go aggressive with your marketing approach. A trustworthy SEO company based out of India will help you define your current and future plans to yield good results. More indexed pages boosted rankings and credible brand of your business made with continuous professional SEO practices will double inquiries, business, and revenues. Any small business can start with two-digit investment in the professional SEO services. There are many SEO agencies in India that offer low budget yet result from oriented Search Engine Optimization services.

surveys from exile

  • CraigWew

    12.04.2018

    п»їThe Importance of Establishing Rapport With the Customer in Real Estate and General Sales

    The importance of establishing rapport with the customer.
    Establishing rapport with a customer has to be earned and must be approached as a very integral part of the sales process.
    In order to get a customer and yourself to relate on a real one to one basis, involves two things!
    First, you will have to be aware and be there! Second you must understand that there are two different stages that will occur during this process.
    A-Be there-what does that mean?
    o Most people don’t really listen to another person as they talk. Generally they are so busy formulating their next answer or statement that they couldn’t possibly really listen.
    o If this sounds like you, being there means shut up and listen!
    B-What is the first or initial stage?
    o Generally you have just a few minutes to establish yourself in the customers mind as someone they want to deal with.
    o When in doubt it is best to first ask questions that will draw them out and talk about themselves.
    o It is also always safe to appear as a professional-I don’t mean stoic or dry, but someone who knows what they are doing and talks and looks the part.
    C-Other stages
    o As time goes on, through conversation and questions they will have, you will either establish your ability or not.
    o Be aware that they will probably be measuring you for a while. The good news is that at some point, if you have been successful at establishing rapport-they will relax and you can both concentrate on finding or selling the home.
    What else can help me develop rapport?
    o By trying to understand different personality types and then by saying and asking the right questions.
    o If you have good rapport (get on the same wave length as the customer) then the selling is basically over, now it’s just a matter of finding the right home or filling out the listing papers.
    What about different personalities
    o Since this is not a book on psychiatry, for now just understand two main types.
    o There are introverted and extroverted people.
    o You know the type. Think about three people you know that fit each classification.
    What about body Language and speech patterns?
    o If they talk fast or slow, try to mimic their speech patterns.
    o If they talk loud or soft, do the same. Are they leaning forward or backward?
    o Needless to say, there are lots of books written on this subject. Just be aware that it is an important factor-especially when you’re sitting in a conference room or at someone’s home discussing a $400,000 deal.
    Developing rapport is a skill that can be learned and improved upon.
    o We all have experienced a salesperson that sold us something and yet we didn’t feel like we were being sold. The reason is he or she, made you feel comfortable to where you trusted them.
    How do we develop rapport?
    o Use your eyes and ears and ask questions. To explain
    o Use the eyes:
    o Look at their dress-their car-their personal possessions and I mean really look at them and decipher what that tells you about them.
    o Use the ears:
    o Listen to what they say and ask questions to get to the bottom of their real MOTIVATION!
    Now during all this conversation, there will probably be one or two things you’ll discover that you have in common with them. (Family, geographical areas, fishing, etc) When you come across common ground, let them know you’re familiarity and then take a minute to discuss it with them.
    What is the Goal?
    o Once they accept you as one of them you’re in position to really have a great experience in the sale as you’re now working together then as a team—you’re no longer the salesman you’re now in an advisory position.
    o Remember, the customer either will or will not allow you to enter his world. If you understand this and really work hard to become empathetic with him/her, you can gain a position of trust. In most cases, you will actually see them relax (body language) when this happens you’re on the way.
    o To illustrate this have you ever given a speech and noticed that as you finally connected with an audience member they will nod in approval. These things may all seem trite but they aren’t.
    In closing, if you can earn a customers trust, selling a product or service is much easier and the experience can be enoyable for everyone involved.
    Always remember that a Win/Win is the best situation.

Работа со строками. Класс string . Конструкторы класса. Функции assign() , append() , insert() , replace() , erase() , find() , rfind() , compare() , c_str() . Примеры

1. Какое назначение класса string в программах на C++?

Класс string предназначен для работы со строками типа char* , которые представляют собой строку с завершающим нулем. Класс string был введенн как альтернативный вариант для работы со строками типа char* . Строки, которые завершаются символом ‘\0’ еще называются C-строками. Поскольку, string есть классом, то можно объявлять объекты этого класса.

2. Какие модули (библиотеки) нужно подключить, чтобы использовать возможности класса string в MS Visual Studio C++?

Чтобы использовать возможности класса string в MS Visual Studio (C++), нужно подключить библиотеку и пространство имен std .

#include using namespace std;
3. Каким образом осуществляется объявление переменной типа string ? Примеры

Объявление переменной типа string осуществляется точно так же как и обычной переменной. Возможный вариант объявления с одновременной инициализацией.

// тип string string s1; // переменная с именем s1 типа string string s2 = "This is a string variable" ; // объявление с инициализацией // использование переменной типа string с оператором присваивания s1 = s2; // s1 = "This is a string variable" s2 = "New text" ;
4. Какие преимущества и недостатки дает использование класса string в сравнении с типом char* ?

Создание нового типа string было обусловлено недостатками работы с строками символов, который демонстрировал тип char* . В сравнении с типом char* тип string имеет следующие основные преимущества:

  • возможность обработки строк стандартными операторами C++ (= , + , = = , <> и т.п.). Как известно, при использовании типа char* даже наиболее простые операции со строками выглядели сложно и требовали написания чрезмерного программного кода;
  • обеспечение лучшей надежности (безопасности) программного кода. Например, при копировании строк, тип string обеспечивает соответствующие действия, которые могут возникнуть в случае, если строка-источник имеет больший размер чем строка-приемник;
  • обеспечение строки, как самостоятельного типа данных. Объявление типа string как строки есть единым для всех переменных в программе, которая обеспечивает непротиворечивость данных.

Основным недостатком типа string в сравнении с типом char* , есть замедленная скорость обработки данных. Это связано с тем, что тип string – это, фактически, контейнерный класс. А работа с классом требует дополнительной реализации программного кода, который, в свою очередь занимает лишнее время.

5. Какие операторы можно использовать с объектами класса string ?

Класс string есть удобен тем, что позволяет удобно манипулировать строками, используя стандартные (перегруженные) операторы.

С объектами класса string можно использовать нижеследующие операторы

  • = – присваивание
  • + – конкатенация (объединение строк)
  • += – присваивание с конкатенацией
  • == – равенство
  • != – неравенство
  • < – меньше
  • <= – меньше или равно
  • > – больше
  • >= – больше или равно
  • – индексация

Пример, который демонстрирует использование вышеприведенных операторов

// тип string, операции над строками string s1 = "s-1" ; string s2 = "s-2" ; string s3; bool b; // операция "=" (присваивание строк) s3 = s1; // s3 = "s-1" // операция "+" - конкатенация строк s3 = s3 + s2; // s3 = "s-1s-2" // операция "+=" - присваивание с конкатенацией s3 = "s-3" ; s3 += "abc" ; // s3 = "s-3abc" // операция "==" - сравнение строк b = s2==s1; // b = false b = s2=="s-2" ; // b = true // операция "!=" - сравнение строк (не равно) s1 = "s1" ; s2 = "s2" ; b = s1 != s2; // b = true // операции "<" и ">" - сравнение строк s1 = "abcd" ; s2 = "de "; b = s1 > s2; // b = false b = s1 < s2; // b = true // операции "<=" и ">=" - сравнение строк (меньше или равно, больше или равно) s1 = "abcd" ; s2 = "ab" ; b = s1 >= s2; // b = true b = s1 <= s2; // b = false b = s2 >= "ab" ; // b = true // операция - индексация char c; s1 = "abcd" ; c = s1; // c = "c" c = s1; // c = "a"
6. Содержит ли класс string конструкторы?

Как и любой класс, класс string имеет ряд конструкторов. Основные из них следующие:

String(); string(const char * str); string(const string & str);

7. Примеры инициализации с помощью конструкторов

Ниже приведены примеры инициализации переменных типа string

String s1("Hello!" ); string s2 = "Hello!" ; // инициализация - конструктор string(const char * str) char * ps = "Hello" ; string s3(ps); // инициализация string s4(s3); // инициализация - конструктор string(const string & str) string s5; // инициализация - конструктор string()

8. Присваивание строк. Функция assign() . Примеры

Чтобы присвоить одну строку другой, можно применить один из двух методов:

  • использовать оператор присваивания ‘=’ ;
  • использовать функцию assign() из класса string .

Функция assign() имеет несколько перегруженных реализаций.

Первый вариант – это вызов функции без параметров

String &assign(void );

В этом случае происходит простое присваивание одной строки другой.

Второй вариант позволяет копировать заданное количество символов из строки:

String &assign(const string & s, size_type st, size_type num);

  • s – объект, из которого берется исходная строка;
  • st – индекс (позиция) в строке, из которой начинается копирование num символов;
  • num – количество символов, которые нужно скопировать из позиции st ;
  • size_type – порядковый тип данных.

Третий вариант функции assign() копирует в вызывающий объект первые num символов строки s :

String & assign(const char * s, size_type num);

  • s – строка, которая завершается символом ‘\0’ ;
  • num – количество символов, которые копируются в вызывающий объект. Копируются первые num символов из строки s .

Ниже приведен пример с разными реализациями функции assign() .

Пример.

// присваивание строк, функция assign() string s1 = "сайт" ; string s2; string s3; char * ps = "сайт" ; s3 = s1; // s3 = "сайт" s2.assign(s1); // s2 = "сайт" s2.assign(s1, 0, 4); // s2 = "best" s2.assign(ps, 8); // s2 = "bestprog"
9. Объединение строк. Функция append() . Пример

Для объединения строк используется функция append() . Для добавления строк также можно использовать операцию ‘+’ , например:

String s1; string s2; s1 = "abc" ; s2 = "def" ; s1 = s1 + s2; // s1 = "abcdef"

Однако, функция append() хорошо подходит, если нужно добавлять часть строки.

Функция имеет следующие варианты реализации:

String &append(const string & s, size_type start); string &append(const char * s, size_type num);

В первом варианте реализации функция получает ссылку на строчный объект s , который добавляется к вызывающему объекту. Во втором варианте реализации функция получает указатель на строку типа const char * , которая завершается символом ‘\0’ .

Пример. Демонстрация работы функции append() .

String s1 = "abcdef" ; s2 = "1234567890" ; append(s2, 3, 4); // s1 = "abcdef4567" char * ps = "1234567890" ; s1 = "abcdef" ; s1.append(ps, 3); // s1 = "abcdef123"

10. Вставка символов в строке. Функция insert() . Пример

Чтобы вставить одну строку в заданную позицию другой строки нужно использовать функцию insert() , которая имеет несколько вариантов реализации.

Первый вариант функции позволяет вставить полностью всю строку s в заданную позицию start вызывающей строки (вызывающего объекта):

String & insert(size_type start, const string &s);

Второй вариант функции позволяет вставить часть (параметры insStart , num ) строки s в заданную позицию start вызывающей строки:

String & insert(size_type start, const string &s, size_type insStart, size_type num);

В вышеприведенных функциях:

  • s – строка, которая вставляется в вызывающую строку;
  • start – позиция в вызывающей строке, из которой осуществляется вставка строки s ;
  • insStart – позиция в строке s , из которой происходит вставка;
  • num – количество символов в строке s , которые вставляются с позиции insStart .
string s1 = "abcdef" ; string s2 = "1234567890" ; s1.insert(3, s2); // s1 = "abc"+"1234567890"+"def"="abc1234567890def" s2.insert(2, s1, 1, 3); // s2 = "12bcd34567890"
11. Замена символов в строке. Функция replace() . Пример

Функция replace() выполняет замену символов в вызывающей строке. Функция имеет следующие варианты реализации:

String &replace(size_type start, size_type num, const string &s); string &replace(size_type start, size_type num, const string &s, size_type replStart, size_type replNum);

В первом варианте реализации вызывающая строка заменяется строкой s . Есть возможность задать позицию (start ) и количество символов (num ) в вызывающей строке, которые нужно заменить строкой s .

Второй вариант функции replace() отличается от первого тем, что позволяет заменять вызывающую строку только частью строки s . В этом случае задаются два дополнительных параметра: позиция replStart и количество символов в строке s , которые образуют подстроку, которая заменяет вызывающую строку.

Пример. Демонстрация работы функции replace() .

String s1 = "abcdef" ; string s2 = "1234567890" ; s2.replace(2, 4, s1); // s2 = "12abcdef7890" s2 = "1234567890" ; s2.replace(3, 2, s1); // s2 = "123abcdef67890" s2 = "1234567890" ; s2.replace(5, 1, s1); // s2 = "12345abcdef7890" // замена символов, функция replace() string s1 = "abcdef" ; string s2 = "1234567890" ; s2.replace(2, 4, s1); // s2 = "12abcdef7890" s2 = "1234567890" ; s2.replace(3, 2, s1); // s2 = "123abcdef67890" s2 = "1234567890" ; s2.replace(5, 1, s1); // s2 = "12345abcdef7890" s2 = "1234567890" ; s2.replace(5, 1, s1, 2, 3); // s2 = "12345cde7890" s2 = "1234567890" ; s2.replace(4, 2, s1, 0, 4); // s2 = "1234abcd7890"

12. Удаление заданного количества символов из строки. Функция erase() . Пример

Для удаления символов из вызывающей строки используется функция erase() :

String & erase(size_type index=0, size_type num = npos);

  • index – индекс (позиция), начиная из которой нужно удалить символы в вызывающей строке;
  • num – количество символов, которые удаляются.

Пример.

String s = "01234567890" ; s.erase(3, 5); // s = "012890" s = "01234567890" ; s.erase(); // s = ""

13. Поиск символа в строке. Функции find() и rfind() . Примеры

В классе string поиск строки в подстроке можно делать двумя способами, которые отличаются направлением поиска:

  • путем просмотра строки от начала до конца с помощью функции find() ;
  • путем просмотра строки от конца к началу функцией rfind() .

Прототип функции find() имеет вид:

Size_type find(const string &s, size_type start = 0) const ;

  • s – подстрока, которая ищется в строке, что вызывает данную функцию. Функция осуществляет поиск первого вхождения строки s . Если подстрока s найдена в строке, что вызвала данную функцию, тогда возвращается позиция первого вхождения. В противном случае возвращается -1;

Прототип функции rfind() имеет вид:

Size_type rfind(const string &s, size_type start = npos) const ;

  • s – подстрока, которая ищется в вызывающей строке. Поиск подстроки в строке осуществляется от конца к началу. Если подстрока s найдена в вызывающей строке, то функция возвращает позицию первого вхождения. В противном случае функция возвращает -1;
  • npos – позиция последнего символа вызывающей строки;
  • start – позиция, из которой осуществляется поиск.

Пример 1. Фрагмент кода, который демонстрирует результат работы функции find()

// тип string, функция find() string s1 = "01234567890" ; string s2 = "345" ; string s3 = "abcd" ; int pos; pos = s1.find(s2); // pos = 3 pos = s1.find(s2, 1); // pos = 3 pos = s1.find("jklmn" , 0); // pos = -1 pos = s1.find(s3); // pos = -1 pos = s2.find(s1); // pos = -1

Пример 2. Демонстрация работы функции rfind() .

// тип string, функции find() и rfind() string s1 = "01234567890" ; string s2 = "345" ; string s3 = "abcd" ; string s4 = "abcd---abcd" ; int pos; pos = s1.rfind(s2); // pos = 3 pos = s1.rfind(s2, 12); // pos = 3 pos = s1.rfind(s2, 3); // pos = 3 pos = s1.rfind(s2, 2); // pos = -1 pos = s2.rfind(s1); // pos = -1 pos = s1.rfind(s3, 0); // pos = -1 // разница между функциями find() и rfind() pos = s4.rfind(s3); // pos = 7 pos = s4.find(s3); // pos = 0
14. Сравнение частей строк. Функция compare() . Пример

Поскольку тип string есть классом, то, чтобы сравнить две строки между собой можно использовать операцию ‘= =’ . Если две строки одинаковы, то результат сравнения будет true . В противном случае, результат сравнения будет false .

Но если нужно сравнить часть одной строки с другой, то для этого предусмотрена функция compare() .

Прототип функции compare() :

int compare(size_type start, size_type num, const string &s) const ;
  • s – строка, которая сравнивается с вызывающей строкой;
  • start – позиция (индекс) в строке s , из которой начинается просмотр символов строки для сравнения;
  • num – количество символов в строке s , которые сравниваются с вызывающей строкой.

Функция работает следующим образом. Если вызывающая строка меньше строки s , то функция возвращает -1 (отрицательное значение). Если вызывающая строка больше строки s , функция возвращает 1 (положительное значение). Если две строки равны, функция возвращает 0.

Пример . Демонстрация работы функции compare() :

// тип string, функция compare() string s1 = "012345" ; string s2 = "0123456789" ; int res; res = s1.compare(s2); // res = -1 res = s1.compare("33333" ); // res = -1 res = s1.compare("012345" ); // res = 0 res = s1.compare("345" ); // res = -1 res = s1.compare(0, 5, s2); // res = -1 res = s2.compare(0, 5, s1); // res = -1 res = s1.compare(0, 5, "012345" ); // res = -1 res = s2.compare(s1); // res = 1 res = s2.compare("456" ); // res = -1 res = s2.compare("000000" ); // res = 1
15. Получение строки с символом конца строки ‘\0’ (char * ). Функция c_str() . Пример

Чтобы получить строку, которая заканчивается символом ‘\0’ используется функция c_str() .

Прототип функции:

const char * c_str() const ;

Функция объявлена с модификатором const . Это означает, что функция не может изменять вызывающий объект (строку).

Пример 1 . Преобразование типа string в const char * .

// тип string, функция c_str() string s = "abcdef" ; const char * ps; ps = s.c_str(); // ps = "abcdef"

Пример 2.

Ниже продемонстрирован перевод строки из string в тип System::String для отображения его в элементе управления типа Label

В программе строки могут определяться следующим образом:

  • как строковые константы;
  • как массивы символов;
  • через указатель на символьный тип;
  • как массивы строк.

Кроме того, должно быть предусмотрено выделение памяти для хранения строки.

Любая последовательность символов, заключенная в двойные кавычки «» , рассматривается как строковая константа .

Для корректного вывода любая строка должна заканчиваться нуль-символом "\0" , целочисленное значение которого равно 0. При объявлении строковой константы нуль-символ добавляется к ней автоматически. Так, последовательность символов, представляющая собой строковую константу, будет размещена в оперативной памяти компьютера, включая нулевой байт.

Под хранение строки выделяются последовательно идущие ячейки оперативной памяти. Таким образом, строка представляет собой массив символов. Для хранения кода каждого символа строки отводится 1 байт.

Для помещения в строковую константу некоторых служебных символов используются символьные комбинации. Так, если необходимо включить в строку символ двойной кавычки, ему должен предшествовать символ «обратный слеш»: ‘\»‘ .

Строковые константы размещаются в статической памяти. Начальный адрес последовательности символов в двойных кавычках трактуется как адрес строки. Строковые константы часто используются для осуществления диалога с пользователем в таких функциях, как printf() .

При определении массива символов необходимо сообщить компилятору требуемый размер памяти.

char m;

Компилятор также может самостоятельно определить размер массива символов, если инициализация массива задана при объявлении строковой константой:

char m2=;
char m3={"Т","и","х","и","е"," ","д","о","л","и","н","ы"," ","п","о","л","н","ы"," ","с","в","е","ж","е","й"," ","м","г","л","о","й","\0" };

В этом случае имена m2 и m3 являются указателями на первые элементы массивов:

  • m2 эквивалентно &m2
  • m2 эквивалентно ‘Г’
  • m2 эквивалентно ‘o’
  • m3 эквивалентно &m3
  • m3 эквивалентно ‘x’

При объявлении массива символов и инициализации его строковой константой можно явно указать размер массива, но указанный размер массива должен быть больше, чем размер инициализирующей строковой константы:

char m2="Горные вершины спят во тьме ночной." ;

Для задания строки можно использовать указатель на символьный тип .

char *m4;

В этом случае объявление массива переменной m4 может быть присвоен адрес массива:

m4 = m3;
*m4 эквивалентно m3="Т"
*(m4+1) эквивалентно m3="и"

Здесь m3 является константой-указателем. Нельзя изменить m3 , так как это означало бы изменение положения (адреса) массива в памяти, в отличие от m4 .

Для указателя можно использовать операцию увеличения (перемещения на следующий символ):

Массивы символьных строк

Иногда в программах возникает необходимость описание массива символьных строк . В этом случае можно использовать индекс строки для доступа к нескольким разным строкам.

char *poet = {"Погиб поэт!", "- невольник чести -" ,
"Пал," , "оклеветанный молвой…" };

В этом случае poet является массивом, состоящим из четырех указателей на символьные строки. Каждая строка символов представляет собой символьный массив, поэтому имеется четыре указателя на массивы. Указатель poet ссылается на первую строку:
*poet эквивалентно "П" ,
*poet[l] эквивалентно "-" .

Инициализация выполняется по правилам, определенным для массивов.
Тексты в кавычках эквивалентны инициализации каждой строки в массиве. Запятая разделяет соседние
последовательности.
Кроме того, можно явно задавать размер строк символов, используя описание, подобное такому:

char poet;

Разница заключается в том, что такая форма задает «прямоугольный» массив, в котором все строки имеют одинаковую длину.

Свободный массив

Описание

сhar *poet;


определяет свободный массив, где длина каждой строки определяется тем указателем, который эту строку инициализирует. Свободный массив не тратит память напрасно.

Операции со строками

Большинство операций языка Си, имеющих дело со строками, работает с указателями. Для размещения в оперативной памяти строки символов необходимо:

  • выделить блок оперативной памяти под массив;
  • проинициализировать строку.

Для выделения памяти под хранение строки могут использоваться функции динамического выделения памяти . При этом необходимо учитывать требуемый размер строки:

char *name;
name = (char *)malloc(10);
scanf("%9s" , name);

Для ввода строки использована функция scanf() , причем введенная строка не может превышать 9 символов. Последний символ будет содержать "\0" .

Функции ввода строк

Для ввода строки может использоваться функция scanf() . Однако функция scanf() предназначена скорее для получения слова, а не строки. Если применять формат "%s" для ввода, строка вводится до (но не включая) следующего пустого символа, которым может быть пробел, табуляция или перевод строки.

Для ввода строки, включая пробелы, используется функция

char * gets(char *);


или её эквивалент

char * gets_s(char *);

В качестве аргумента функции передается указатель на строку, в которую осуществляется ввод. Функция просит пользователя ввести строку, которую она помещает в массив, пока пользователь не нажмет Enter .

Функции вывода строк

Для вывода строк можно воспользоваться рассмотренной ранее функцией

printf("%s" , str); // str - указатель на строку

или в сокращенном формате

printf(str);

Для вывода строк также может использоваться функция

int puts (char *s);

которая печатает строку s и переводит курсор на новую строку (в отличие от printf() ). Функция puts() также может использоваться для вывода строковых констант, заключенных в кавычки.

Функция ввода символов

Для ввода символов может использоваться функция

char getchar();


которая возвращает значение символа, введенного с клавиатуры. Указанная функция использовалась в рассмотренных ранее примерах для задержки окна консоли после выполнения программы до нажатия клавиши.

Функция вывода символов

Для вывода символов может использоваться функция

char putchar(char );


которая возвращает значение выводимого символа и выводит на экран символ, переданный в качестве аргумента.

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

#include
#include
#include
int main() {
char s, sym;
int count, i;
system("chcp 1251" );
system("cls" );
printf("Введите строку: " );
gets_s(s);
printf("Введите символ: " );
sym = getchar();
count = 0;
for (i = 0; s[i] != "\0" ; i++)
{
if (s[i] == sym)
count++;
}
printf("В строке\n" );
puts(s); // Вывод строки
printf("символ " );
putchar(sym); // Вывод символа
printf(" встречается %d раз" , count);
getchar(); getchar();
return 0;
}

Результат выполнения

Основные функции стандартной библиотеки string.h

Основные функции стандартной библиотеки string.h приведены в таблице.

Функция Описание

char *strcat(char *s1, char *s2)

присоединяет s2 к s1, возвращает s1

char *strncat(char *s1, char *s2, int n)

присоединяет не более n символов s2 к s1, завершает строку символом "\0", возвращает s1

char *strсpy(char *s1, char *s2)

копирует строку s2 в строку s1, включая "\0", возвращает s1
);
strncpy(m3, m1, 6); // не добавляет "\0" в конце строки
puts("Результат strncpy(m3, m1, 6)" );
puts(m3);
strcpy(m3, m1);
puts("Результат strcpy(m3, m1)" );
puts(m3);
puts("Результат strcmp(m3, m1) равен" );
printf("%d" , strcmp(m3, m1));
strncat(m3, m2, 5);
puts("Результат strncat(m3, m2, 5)" );
puts(m3);
strcat(m3, m2);
puts("Результат strcat(m3, m2)" );
puts(m3);
puts("Количество символов в строке m1 равно strlen(m1) : " );
printf("%d\n" , strlen(m1));
_strnset(m3, "f" , 7);
puts("Результат strnset(m3, "f" , 7)" );
puts(m3);
_strset(m3, "k" );
puts("Результат strnset(m3, "k" )" );
puts(m3);
getchar();
return 0;
}

Результат выполнения