C первые символы строки. Строки в си. Введение

Faq 13.08.2019
Faq

Объявление строк

Строка в языке Си представляет собой одномерный массив символов, последним элементом которой является символ конца строки – нуль (строка, завершающаяся нулем, то есть NULL terminated string).

Объявление переменной типа строка в языке Си возможно тремя способами, два из которых инициализируют строку во время объявления.

Первый способ:

Объявления массива символов (не забудьте добавить место для завершающего нуля):

Char s;

Второй способ:

Присвоить строковой переменной начальное значение (при этом длину строки компилятор может вычислить сам):

Char s = "Пример инициализации строки";

Справа от знака присваивания записана строковая константа. В конце строки автоматически добавляется ноль (‘\0’). Константы символьных строк помещаются в класс статической памяти.

Третий способ:

Неявное указание, что используется массив. В левой части от знака присваивания указывается указатель на символ:

Char *s="Второй вариант инициализации";

Переменная s будет указателем на то место в оперативной памяти, где располагается строковая константа. В такой форме записи кроется потенциальная ошибка, заключающаяся в том, что указатель на символ часто называют строкой. Представленная ниже запись – это только указатель на символ, так как для размещения строки место не предусмотрено:

Char *s;

Ввод строки со стандартного устройства ввода (клавиатуры)

Для работы со строками есть набор функций. Для ввода со стандартного устройства ввода (клавиатуры) чаще всего используются библиотечные функциями из модуля стандартного ввода-вывода: scanf и gets .

Для ввода строки с помощью функции scanf , использует формат «%s » , причем обратите внимание на то, что перед идентификатором строки не используется знак адреса «& » , так как одномерный массив уже представлен указателем на его начало:

Scanf("%s", s);

Функция gets() считывает символы до тех пор, пока не достигнет символа перехода на новую строку. Функция принимает все символы вплоть до символа перевода строки, но не включает его. К концу строки добавляется завершающий ноль (‘\0’). Функция gets() помещает считанную с клавиатуры последовательность символов в параметр типа строка и возвращает указатель на эту строку (если операция завершилась успешно), или NULL (в случае ошибки). В приведенном ниже примере при успешном завершении операции, на экран будет выведено две одинаковые строки:

#include int main() { char s; char *p; p=gets(s); printf(" \n Введена строка %s. ",s); if (p) printf(" \n Введена строка %s. ",p); return 0; }

Попутно заметим, что функция gets часто используется для ввода лю-бых данных с клавиатуры в виде строки с целью дальнейшего преобразования функцией sscanf к нужному формату или для предварительного анализа вводимых данных, например:

#include #include #include int main() { char s; int x, err; do { printf(" \n Введите целое число -> "); gets(s); err=sscanf(s, "%d",&x); if (err!=1) printf(" \n Ошибка ввода. "); } while (err!=1); printf("\n Введено целое число -> %d", x); return 0; }

Вывод строк на стандартное устройство вывода (экран монитора)

Для вывода строк на стандартное устройство вывода (экран монитора) можно использовать две функции printf и puts . В функции printf в качестве формата передается «%s». Удобство использования этой функции заключается в том, что помимо строки можно сразу выводит данные других типов. Особенность функции puts заключается в том, что после вывода строки автоматически происходит переход на следующую строку.

Функции для работы со строками

Для преобразования строк в языке Си предусмотрена библиотека string. Каждая из функций имеет свой формат записи (прототип).

Наиболее используемые функции рассмотрены в этой статье. — читать

Пример программ(листинг) работающей со строками

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

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

Разговор о программировании под 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), с кем не бывает). Собственно говоря, это и сподвигло меня на создание сего творения, которое вы только что прочитали.

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

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

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

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

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

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

Для корректного вывода любая строка должна заканчиваться нуль-символом "\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;
}

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

Теги: Си строки. Char array.

Строки в си. Введение.

Э то вводная статья по строкам в си. Более подробное описание и примеры будут, когда мы научимся работать с памятью и указателями. В компьютере все значения хранятся в виде чисел. И строки тоже, там нет никаких символов и букв. Срока представляет собой массив чисел. Каждое число соответствует определённому символу, который берётся из таблицы кодировки. При выводе на экран символ отображается определённым образом.
Для хранения строк используются массивы типа char. Ещё раз повторюсь – тип char – числовой, он хранит один байт данных. Но в соответствии с таблицей кодировки каждое из этих чисел связано с символом. И в обратную сторону – каждый символ определяется своим порядковым номером в таблице кодировки.
Например

#include #include void main() { char c = "A"; int i = 65; printf("display as char %c\n", c); printf("display as int %d\n", c); printf("display as char %c\n", i); printf("display as char %d\n", i); getch(); }

Мы создали две переменные, одна типа char , другая int . Литера "A" имеет числовое значение 65. Это именно литера, а не строка, поэтому окружена одинарными кавычками. Мы можем вывести её на печать как букву

Printf("display as char %c\n", c);

Тогда будет выведено
A
Если вывести её как число, то будет
65
Точно также можно поступить и с числом 65, которое хранится в переменной типа int .
Спецсимволы также имеют свой номер

#include #include void main() { printf("%c", "\a"); printf("%d", "\a"); printf("%c", 7); getch(); }

Здесь будет сначала "выведен" звуковой сигнал, затем его числовое значение, затем опять звуковой сигнал.
Строка в си – это массив типа char , последний элемент которого хранит терминальный символ "\0". Числовое значение этого символа 0, поэтому можно говорить, что массив оканчивается нулём.
Например

#include #include void main() { char word; word = "A"; word = "B"; word = "C"; word = "\0"; //word = 0; эквивалентно printf("%s", word); getch(); }

Для вывода использовался ключ %s. При этом строка выводится до первого терминального символа, потому что функция printf не знает размер массива word.
Если в этом примере не поставить

Word = "\0";

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

#include #include void main() { char word = "ABC"; char text = {"H", "E", "L", "L", "O"}; printf("%s\n", word); printf("%s", text); getch(); }

В данном случае всё корректно. Строка "ABC" заканчивается нулём, и ею мы инициализируем массив word. Строка text инициализируется побуквенно, все оставшиеся символы, как следует из главы про массивы, заполняются нулями.

Чтение строк

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

#include #include void main() { char buffer; scanf("%19s", buffer); printf("%s", buffer); getch(); }

В данном случае количество введённых символов ограничено 19, а размер буфера на 1 больше, так как необходимо хранить терминальный символ. Напишем простую программу, которая запрашивает у пользователя строку и возвращает её длину.

#include #include void main() { char buffer; unsigned len = 0; scanf("%127s", buffer); while (buffer != "\0") { len++; } printf("length(%s) == %d", buffer, len); getch(); }

Так как числовое значение символа "\0" равно нулю, то можно записать

While (buffer != 0) { len++; }

Или, ещё короче

While (buffer) { len++; }

Теперь напишем программу, которая запрашивает у пользователя два слова и сравнивает их

#include #include /* Результатом сравнения будет число 0 если слова равны 1 если первое слово больше второго в лексикографическом порядке -1 если второе слово больше */ void main() { char firstWord; //Первое слово char secondWord; //Второе слово unsigned i; //Счётчик int cmpResult = 0; //Результат сравнения scanf("%127s", firstWord); scanf("%127s", secondWord); for (i = 0; i < 128; i++) { if (firstWord[i] > secondWord[i]) { //Больше даже если второе слово уже закончилось, потому что //тогда оно заканчивается нулём cmpResult = 1; break; } else if (firstWord[i] < secondWord[i]) { cmpResult = -1; break; } } printf("%d", cmpResult); getch(); }

Так как каждая буква имеет числовое значение, то их можно сравнивать между собой как числа. Кроме того, обычно (но не всегда!) буквы в таблицах кодировок расположены по алфавиту. Поэтому сортировка по числовому значению также будет и сортировкой по алфавиту.

34

--- Руководство по C# --- Строки

С точки зрения регулярного программирования строковый тип данных string относится к числу самых важных в C#. Этот тип определяет и поддерживает символьные строки. В целом ряде других языков программирования строка представляет собой массив символов. А в C# строки являются объектами. Следовательно, тип string относится к числу ссылочных.

Построение строк

Самый простой способ построить символьную строку - воспользоваться строковым литералом . Например, в следующей строке кода переменной ссылки на строку str присваивается ссылка на строковый литерал:

String str = "Пример строки";

В данном случае переменная str инициализируется последовательностью символов "Пример строки". Объект типа string можно также создать из массива типа char. Например:

Char chararray = {"e", "x", "a", "m", "p", "l", "e"}; string str = new string(chararray);

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

Постоянство строк

Как ни странно, содержимое объекта типа string не подлежит изменению. Это означает, что однажды созданную последовательность символов изменить нельзя. Но данное ограничение способствует более эффективной реализации символьных строк. Поэтому этот, на первый взгляд, очевидный недостаток на самом деле превращается в преимущество. Так, если требуется строка в качестве разновидности уже имеющейся строки, то для этой цели следует создать новую строку, содержащую все необходимые изменения. А поскольку неиспользуемые строковые объекты автоматически собираются в "мусор", то о дальнейшей судьбе ненужных строк можно даже не беспокоиться.

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

Рассмотрим пример:

Static void addNewString() { string s = "This is my stroke"; s = "This is new stroke"; }

Скомпилируем приложение и загрузим результирующую сборку в утилиту ildasm.exe . На рисунке показан CIL-код, который будет сгенерирован для метода void addNewString():

Обратите внимание на наличие многочисленных вызовов кода операции ldstr (загрузка строки). Этот код операции ldstr в CIL предусматривает выполнение загрузки нового объекта string в управляемую кучу. В результате предыдущий объект, в котором содержалось значение "This is my stroke", будет в конечном итоге удален сборщиком мусора.

Работа со строками

В классе System.String предоставляется набор методов для определения длины символьных данных, поиска подстроки в текущей строке, преобразования символов из верхнего регистра в нижний и наоборот, и т.д. Далее мы рассмотрим этот класс более подробно.

Поле, индексатор и свойство класса String

В классе String определено единственное поле:

Public static readonly string Empty;

Поле Empty обозначает пустую строку, т.е. такую строку, которая не содержит символы. Этим оно отличается от пустой ссылки типа String, которая просто делается на несуществующий объект.

Помимо этого, в классе String определен единственный индексатор, доступный только для чтения:

Public char this { get; }

Этот индексатор позволяет получить символ по указанному индексу. Индексация строк, как и массивов, начинается с нуля. Объекты типа String отличаются постоянством и не изменяются, поэтому вполне логично, что в классе String поддерживается индексатор, доступный только для чтения.

И наконец, в классе String определено единственное свойство, доступное только для чтения:

Public int Length { get; }

Свойство Length возвращает количество символов в строке. В примере ниже показано использование индексатора и свойства Length:

Using System; class Example { static void Main() { string str = "Простая строка"; // Получить длину строки и 6й символ в строке используя индексатор Console.WriteLine("Длина строки - {0}, 6й символ - "{1}"", str.Length, str); } }

Операторы класса String

В классе String перегружаются два следующих оператора: == и!=. Оператор == служит для проверки двух символьных строк на равенство. Когда оператор == применяется к ссылкам на объекты, он обычно проверяет, делаются ли обе ссылки на один и тот же объект. А когда оператор == применяется к ссылкам на объекты типа String, то на предмет равенства сравнивается содержимое самих строк. Это же относится и к оператору!=. Когда он применяется к ссылкам на объекты типа String, то на предмет неравенства сравнивается содержимое самих строк. В то же время другие операторы отношения, в том числе =, сравнивают ссылки на объекты типа String таким же образом, как и на объекты других типов. А для того чтобы проверить, является ли одна строка больше другой, следует вызвать метод Compare(), определенный в классе String.

Как станет ясно дальше, во многих видах сравнения символьных строк используются сведения о культурной среде. Но это не относится к операторам == и!=. Ведь они просто сравнивают порядковые значения символов в строках. (Иными словами, они сравнивают двоичные значения символов, не видоизмененные нормами культурной среды, т.е. региональными стандартами.) Следовательно, эти операторы выполняют сравнение строк без учета регистра и настроек культурной среды.

Методы класса String

В следующей таблице перечислены некоторые наиболее интересные методы этого класса, сгруппированные по назначению:

Методы работы со строками
Метод Структура и перегруженные версии Назначение
Сравнение строк
Compare() public static int Compare(string strA, string strB)

Public static int Compare(string strA, string strB, bool ignoreCase)

Public static int Compare(string strA, string strB, StringComparison comparisonType)

Public static int Compare(string strA, string strB, bool ignoreCase, CultureInfo culture)

Статический метод, сравнивает строку strA со строкой strB. Возвращает положительное значение, если строка strA больше строки strB; отрицательное значение, если строка strA меньше строки strB; и нуль, если строки strA и strB равны. Сравнение выполняется с учетом регистра и культурной среды.

Если параметр ignoreCase принимает логическое значение true, то при сравнении не учитываются различия между прописным и строчным вариантами букв. В противном случае эти различия учитываются.

Параметр comparisonType определяет конкретный способ сравнения строк. Класс CultureInfo определен в пространстве имен System.Globalization.

public static int Compare(string strA, int indexA, string strB, int indexB, int length)

Public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase)

Public static int Compare(string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)

Public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, CultureInfo culture)

Сравнивает части строк strA и strB. Сравнение начинается со строковых элементов strA и strB и включает количество символов, определяемых параметром length. Метод возвращает положительное значение, если часть строки strA больше части строки strB; отрицательное значение, если часть строки strA меньше части строки strB; и нуль, если сравниваемые части строк strA и strB равны. Сравнение выполняется с учетом регистра и культурной среды.

CompareOrdinal() public static int CompareOrdinal(string strA, string strB)

Public static int CompareOrdinal(string strA, int indexA, string strB, int indexB, int count)

Делает то же, что и метод Compare(), но без учета локальных установок

CompareTo() public int CompareTo(object value)

Сравнивает вызывающую строку со строковым представлением объекта value. Возвращает положительное значение, если вызывающая строка больше строки value; отрицательное значение, если вызывающая строка меньше строки value; и нуль, если сравниваемые строки равны

public int CompareTo(string strB)

Сравнивает вызывающую строку со строкой strB

Equals() public override bool Equals(object obj)

Возвращает логическое значение true, если вызывающая строка содержит ту же последовательность символов, что и строковое представление объекта obj. Выполняется порядковое сравнение с учетом регистра, но без учета культурной среды

public bool Equals(string value)

Public bool Equals(string value, StringComparison comparisonType)

Возвращает логическое значение true, если вызывающая строка содержит ту же последовательность символов, что и строка value. Выполняется порядковое сравнение с учетом регистра, но без учета культурной среды. Параметр comparisonType определяет конкретный способ сравнения строк

public static bool Equals(string a, string b)

Public static bool Equals(string a, string b, StringComparison comparisonType)

Возвращает логическое значение true, если строка a содержит ту же последовательность символов, что и строка b . Выполняется порядковое сравнение с учетом регистра, но без учета культурной среды. Параметр comparisonType определяет конкретный способ сравнения строк

Конкатенация (соединение) строк
Concat() public static string Concat(string str0, string str1);

public static string Concat(params string values);

Комбинирует отдельные экземпляры строк в одну строку (конкатенация)
Поиск в строке
Contains() public bool Contains(string value) Метод, который позволяет определить, содержится ли в строке определенная подстрока (value)
StartsWith() public bool StartsWith(string value)

Public bool StartsWith(string value, StringComparison comparisonType)

Возвращает логическое значение true, если вызывающая строка начинается с подстроки value. В противном случае возвращается логическое значение false. Параметр comparisonType определяет конкретный способ выполнения поиска

EndsWith() public bool EndsWith(string value)

Public bool EndsWith(string value, StringComparison comparisonType)

Возвращает логическое значение true, если вызывающая строка оканчивается подстрокой value. В противном случае возвращает логическое значение false. Параметр comparisonType определяет конкретный способ поиска

IndexOf() public int IndexOf(char value)

Public int IndexOf(string value)

Находит первое вхождение заданной подстроки или символа в строке. Если искомый символ или подстрока не обнаружены, то возвращается значение -1

public int IndexOf(char value, int startIndex)

Public int IndexOf(string value, int startIndex)

Public int IndexOf(char value, int startIndex, int count)

Public int IndexOf(string value, int startIndex, int count)

Возвращает индекс первого вхождения символа или подстроки value в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count (если указан). Метод возвращает значение -1, если искомый символ или подстрока не обнаружен

LastIndexOf() Перегруженные версии аналогичны методу IndexOf()

То же, что IndexOf, но находит последнее вхождение символа или подстроки, а не первое

IndexOfAny() public int IndexOfAny(char anyOf)

Public int IndexOfAny(char anyOf, int startIndex)

Public int IndexOfAny(char anyOf, int startIndex, int count)

Возвращает индекс первого вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке. Поиск начинается с элемента, указываемого по индексу startIndex, и охватывает число элементов, определяемых параметром count (если они указаны). Метод возвращает значение -1, если не обнаружено совпадение ни с одним из символов из массива anyOf. Поиск осуществляется порядковым способом

LastIndexOfAny Перегруженные версии аналогичны методу IndexOfAny()

Возвращает индекс последнего вхождения любого символа из массива anyOf, обнаруженного в вызывающей строке

Разделение и соединение строк
Split public string Split(params char separator)

Public string Split(params char separator, int count)

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

В первой форме метода Split() вызывающая строка разделяется на составные части. В итоге возвращается массив, содержащий подстроки, полученные из вызывающей строки. Символы, ограничивающие эти подстроки, передаются в массиве separator. Если массив separator пуст или ссылается на пустую строку, то в качестве разделителя подстрок используется пробел. А во второй форме данного метода возвращается количество подстрок, определяемых параметром count.

public string Split(params char separator, StringSplitOptions options)

Public string Split(string separator, StringSplitOptions options)

Public string Split(params char separator, int count, StringSplitOptions options)

Public string Split(string separator, int count, StringSplitOptions options)

В двух первых формах метода Split() вызывающая строка разделяется на части и возвращается массив, содержащий подстроки, полученные из вызывающей строки. Символы, разделяющие эти подстроки, передаются в массиве separator. Если массив separator пуст, то в качестве разделителя используется пробел. А в третьей и четвертой формах данного метода возвращается количество строк, ограничиваемое параметром count.

Но во всех формах параметр options обозначает конкретный способ обработки пустых строк, которые образуются в том случае, если два разделителя оказываются рядом. В перечислении StringSplitOptions определяются только два значения: None и RemoveEmptyEntries . Если параметр options принимает значение None, то пустые строки включаются в конечный результат разделения исходной строки. А если параметр options принимает значение RemoveEmptyEntries, то пустые строки исключаются из конечного результата разделения исходной строки.

Join() public static string Join(string separator, string value)

Public static string Join(string separator, string value, int startIndex, int count)

Строит новую строку, комбинируя содержимое массива строк.

В первой форме метода Join() возвращается строка, состоящая из сцепляемых подстрок, передаваемых в массиве value. Во второй форме также возвращается строка, состоящая из подстрок, передаваемых в массиве value, но они сцепляются в определенном количестве count, начиная с элемента массива value. В обеих формах каждая последующая строка отделяется от предыдущей разделительной строкой, определяемой параметром separator.

Заполнение и обрезка строк
Trim() public string Trim()

Public string Trim(params char trimChars)

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

В первой форме метода Trim() из вызывающей строки удаляются начальные и конечные пробелы. А во второй форме этого метода удаляются начальные и конечные вхождения в вызывающей строке символов из массива trimChars. В обеих формах возвращается получающаяся в итоге строка.

PadLeft() public string PadLeft(int totalWidth)

Public string PadLeft(int totalWidth, char paddingChar)

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

В первой форме метода PadLeft() вводятся пробелы с левой стороны вызывающей строки, чтобы ее общая длина стала равной значению параметра totalWidth. А во второй форме данного метода символы, обозначаемые параметром paddingChar, вводятся с левой стороны вызывающей строки, чтобы ее общая длина стала равной значению параметра totalWidth. В обеих формах возвращается получающаяся в итоге строка. Если значение параметра totalWidth меньше длины вызывающей строки, то возвращается копия неизмененной вызывающей строки.

PadRight() Аналогично PadLeft()

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

Вставка, удаление и замена строк
Insert() public string Insert(int startIndex, string value)

Используется для вставки одной строки в другую, где value обозначает строку, вставляемую в вызывающую строку по индексу startIndex. Метод возвращает получившуюся в итоге строку.

Remove() public string Remove(int startIndex)

Public string Remove(int startIndex, int count)

Используется для удаления части строки. В первой форме метода Remove() удаление выполняется, начиная с места, указываемого по индексу startIndex, и продолжается до конца строки. А во второй форме данного метода из строки удаляется количество символов, определяемое параметром count, начиная с места, указываемого по индексу startIndex.

Replace() public string Replace(char oldChar, char newChar)

Public string Replace(string oldValue, string newValue)

Используется для замены части строки. В первой форме метода Replace() все вхождения символа oldChar в вызывающей строке заменяются символом newChar. А во второй форме данного метода все вхождения строки oldValue в вызывающей строке заменяются строкой newValue.

Смена регистра
ToUpper() public string ToUpper()

Делает заглавными все буквы в вызывающей строке.

ToLower() public string ToLower()

Делает строчными все буквы в вызывающей строке.

Получение подстроки из строки
Substring() public string Substring(int startIndex)

Public string Substring(int startIndex, int length)

В первой форме метода Substring() подстрока извлекается, начиная с места, обозначаемого параметром startIndex, и до конца вызывающей строки. А во второй форме данного метода извлекается подстрока, состоящая из количества символов, определяемых параметром length, начиная с места, обозначаемого параметром startIndex.

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

Using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string args) { // Сравним первые две строки string s1 = "это строка"; string s2 = "это текст, а это строка"; if (String.CompareOrdinal(s1, s2) != 0) Console.WriteLine("Строки s1 и s2 не равны"); if (String.Compare(s1, 0, s2, 13, 10, true) == 0) Console.WriteLine("При этом в них есть одинаковый текст"); // Конкатенация строк Console.WriteLine(String.Concat("\n" + "Один, два ","три, четыре")); // Поиск в строке // Первое вхождение подстроки if (s2.IndexOf("это") != -1) Console.WriteLine("Слово \"это\" найдено в строке, оно "+ "находится на: {0} позиции", s2.IndexOf("это")); // Последнее вхождение подстроки if (s2.LastIndexOf("это") != -1) Console.WriteLine("Последнее вхождение слова \"это\" находится " + "на {0} позиции", s2.LastIndexOf("это")); // Поиск из массива символов char myCh = {"Ы","х","т"}; if (s2.IndexOfAny(myCh) != -1) Console.WriteLine("Один из символов из массива ch "+ "найден в текущей строке на позиции {0}", s2.IndexOfAny(myCh)); // Определяем начинается ли строка с заданной подстроки if (s2.StartsWith("это текст") == true) Console.WriteLine("Подстрока найдена!"); // Определяем содержится ли в строке подстрока // на примере определения ОС пользователя string myOS = Environment.OSVersion.ToString(); if (myOS.Contains("NT 5.1")) Console.WriteLine("Ваша операционная система Windows XP"); else if (myOS.Contains("NT 6.1")) Console.WriteLine("Ваша операционная система Windows 7"); Console.ReadLine(); } } }

Немного о сравнении строк в C#

Вероятно, из всех операций обработки символьных строк чаще всего выполняется сравнение одной строки с другой. Прежде чем рассматривать какие-либо методы сравнения строк, следует подчеркнуть следующее: сравнение строк может быть выполнено в среде.NET Framework двумя основными способами:

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

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

В силу отличий способов сравнения строк с учетом культурной среды и порядкового сравнения, а также последствий каждого такого сравнения настоятельно рекомендуется руководствоваться лучшими методиками, предлагаемыми в настоящее время корпорацией Microsoft. Ведь выбор неверного способа сравнения строк может привести к неправильной работе программы, когда она эксплуатируется в среде, отличающей от той, в которой она разработана.

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

В классе String предоставляются самые разные методы сравнения строк, которые перечислены в таблице выше. Наиболее универсальным среди них является метод Compare(). Он позволяет сравнивать две строки полностью или частично, с учетом или без учета регистра, способа сравнения, определяемого параметром типа StringComparison , а также сведений о культурной среде, предоставляемых с помощью параметра типа CultureInfo .

Те перегружаемые варианты метода Compare(), которые не содержат параметр типа StringComparison, выполняют сравнение символьных строк с учетом регистра и культурной среды. А в тех перегружаемых его вариантах, которые не содержат параметр типа CultureInfo, сведения о культурной среде определяются текущей средой выполнения.

Тип StringComparison представляет собой перечисление, в котором определяются значения, приведенные в таблице ниже. Используя эти значения, можно организовать сравнение строк, удовлетворяющее потребностям конкретного приложения. Следовательно, добавление параметра типа StringComparison расширяет возможности метода Compare() и других методов сравнения, например, Equals(). Это дает также возможность однозначно указывать способ предполагаемого сравнения строк.

В силу имеющих отличий между сравнением строк с учетом культурной среды и порядковым сравнением очень важно быть предельно точным в этом отношении.

Значения, определяемые в перечислении StringComparison
Значение Описание
CurrentCulture Сравнение строк производится с использованием текущих настроек параметров культурной среды
CurrentCultureIgnoreCase Сравнение строк производится с использованием текущих настроек параметров культурной среды, но без учета регистра
InvariantCulture Сравнение строк производится с использованием неизменяемых, т.е. универсальных данных о культурной среде
InvariantCultureIgnoreCase Сравнение строк производится с использованием неизменяемых, т.е. универсальных данных о культурной среде и без учета регистра
Ordinal Сравнение строк производится с использованием порядковых значений символов в строке. При этом лексикографический порядок может нарушиться, а условные обозначения, принятые в отдельной культурной среде, игнорируются
OrdinalIgnoreCase Сравнение строк производится с использованием порядковых значений символов в строке, но без учета регистра

В любом случае метод Compare() возвращает отрицательное значение, если первая сравниваемая строка оказывается меньше второй; положительное значение, если первая сравниваемая строка больше второй; и наконец, нуль, если обе сравниваемые строки равны. Несмотря на то что метод Compare() возвращает нуль, если сравниваемые строки равны, для определения равенства символьных строк, как правило, лучше пользоваться методом Equals() или же оператором ==.

Дело в том, что метод Compare() определяет равенство сравниваемых строк на основании порядка их сортировки. Так, если выполняется сравнение строк с учетом культурной среды, то обе строки могут оказаться одинаковыми по порядку их сортировки, но не равными по существу. По умолчанию равенство строк определяется в методе Equals(), исходя из порядковых значений символов и без учета культурной среды. Следовательно, по умолчанию обе строки сравниваются в этом методе на абсолютное, посимвольное равенство подобно тому, как это делается в операторе ==.

Несмотря на большую универсальность метода Compare(), для простого порядкового сравнения символьных строк проще пользоваться методом CompareOrdinal(). И наконец, следует иметь в виду, что метод CompareTo() выполняет сравнение строк только с учетом культурной среды.

В приведенной ниже программе демонстрируется применение методов Compare(), Equals(), CompareOrdinal(), а также операторов == и!= для сравнения символьных строк. Обратите внимание на то, что два первых примера сравнения наглядно демонстрируют отличия между сравнением строк с учетом культурной среды и порядковым сравнением в англоязычной среде:

Using System; class Example { static void Main() { string str1 = "alpha"; string str2 = "Alpha"; string str3 = "Beta"; string str4 = "alpha"; string str5 = "alpha, beta"; int result; // Сначала продемонстрировать отличия между сравнением строк // с учетом культурной среды и порядковым сравнением result = String.Compare(str1, str2, StringComparison.CurrentCulture); Console.Write("Сравнение строк с учетом культурной среды: "); if (result 0) Console.WriteLine(str1 + " больше " + str2); else Console.WriteLine(str1 + " равно " + str2); result = String.Compare(str1, str2, StringComparison.Ordinal); Console.Write("Порядковое сравнение строк: "); if (result 0) Console.WriteLine(str1 + " больше " + str2); else Console.WriteLine(str1 + " равно " + str4); // Использовать метод CompareOrdinal() result = String.CompareOrdinal(str1, str2); Console.Write("Сравнение строк методом CompareOrdinal():\n"); if (result 0) Console.WriteLine(str1 + " больше " + str2); else Console.WriteLine(str1 + " равно " + str4); Console.WriteLine(); // Определить равенство строк с помощью оператора == // Это порядковое сравнение символьных строк if (str1 == str4) Console.WriteLine(str1 + " == " + str4); // Определить неравенство строк с помощью оператора!= if(str1 != str3) Console.WriteLine(str1 + " != " + str3); if(str1 != str2) Console.WriteLine(str1 + " != " + str2); Console.WriteLine(); // Выполнить порядковое сравнение строк без учета регистра, // используя метод Equals() if(String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) Console.WriteLine("Сравнение строк методом Equals() с " + "параметром OrdinalIgnoreCase:\n" + str1 + " равно " + str2); Console.WriteLine (); // Сравнить части строк if(String.Compare(str2, 0, str5, 0, 3, StringComparison.CurrentCulture) > 0) { Console.WriteLine("Сравнение строк с учетом текущей культурной среды:" + "\n3 первых символа строки " + str2 + " больше, чем 3 первых символа строки " + str5); } } }

Выполнение этой программы приводит к следующему результату:



Рекомендуем почитать

Наверх