Строки в языке C
Строки в C, как и в большинстве языков программирования высокого уровня рассматриваются как отдельный тип, входящий в систему базовых типов языка. Так как язык C по своему происхождению является языком системного программирования, то строковый тип данных в C как таковой отсутствует, а в качестве строк в С используются обычные массивы символов.
Исторически сложилось два представления формата строк:
Формат ANSI устанавливает, что значением первой позиции в строке является ее длина, а затем следуют сами символы строки. Например, представление строки «Моя строка!» будет следующим:
11 ‘М’ ‘о’ ‘я’ ‘ ‘ ‘с’ ‘т’ ‘р’ ‘о’ ‘к’ ‘а’ ‘!’
В строках с завершающим нулем, значащие символы строки указываются с первой позиции, а признаком завершения строки является значение ноль. Представление рассмотренной ранее строки в этом формате имеет вид:
‘М’ ‘о’ ‘я’ ‘ ‘ ‘с’ ‘т’ ‘р’ ‘о’ ‘к’ ‘а’ ‘!’ 0
Объявление строк в C
Строки реализуются посредством массивов символов. Поэтому объявление ASCII строки имеет следующий синтаксис:
char имя[длина];
Объявление строки в С имеет тот же синтаксис, что и объявление одномерного символьного массива. Длина строки должна представлять собой целочисленное значение (в стандарте C89 – константа, в стандарте C99 может быть выражением). Длина строки указывается с учетом одного символа на хранение завершающего нуля, поэтому максимальное количество значащих символов в строке на единицу меньше ее длины. Например, строка может содержать максимально двадцать символов, если объявлена следующим образом:
char str[21]; Инициализация строки в С осуществляется при ее объявлении, используя следующий синтаксис:
char str[длина] = строковый литерал;
Строковый литерал – строка ASCII символов заключенных в двойные кавычки. Примеры объявления строк с инициализацией:
char str1[20] = «Введите значение: «, str2[20] = «»;
const char message[] = «Сообщение об ошибке!»;
Работа со строками в С
Так как строки на языке С являются массивами символов, то к любому символу строки можно обратиться по его индексу. Для этого используется синтаксис обращения к элементу массива, поэтому первый символ в строке имеет индекс ноль. Например, в следующем фрагменте программы в строке str осуществляется замена всех символов ‘a’ на символы ‘A’ и наоборот.
Массивы строк в С
Объявление массивов строк в языке С также возможно. Для этого используются двумерные массивы символов, что имеет следующий синтаксис:
char имя[количество][длина];
Первым размером матрицы указывается количество строк в массиве, а вторым – максимальная (с учетом завершающего нуля) длина каждой строки. Например, объявление массива из пяти строк максимальной длиной 30 значащих символов будет иметь вид:
Число строковых литералов должно быть меньше или равно количеству строк в массиве. Если число строковых литералов меньше размера массива, то все остальные элементы инициализируются пустыми строками. Длина каждого строкового литерала должна быть строго меньше значения длины строки (для записи завершающего нуля).
При объявлении массивов строк с инициализацией допускается не указывать количество строк в квадратных скобках. В таком случае, количество строк в массиве будет определено автоматически по числу инициализирующих строковых литералов.
Например, массив из семи строк:
Функции для работы со строками в С
Все библиотечные функции, предназначенные для работы со строками, можно разделить на три группы:
Ввод и вывод строк в С
Для ввода и вывода строковой информации можно использовать функции форматированного ввода и вывода (printf и scanf). Для этого в строке формата при вводе или выводе строковой переменной необходимо указать спецификатор типа %s. Например, ввод и последующий вывод строковой переменной будет иметь вид:
char str[31] = «»;
printf(«Введите строку: «);
scanf(«%30s”,str);
printf(«Вы ввели: %s”,str);
Недостатком функции scanf при вводе строковых данных является то, что символами разделителями данной функции являются:
Поэтому, используя данную функцию невозможно ввести строку, содержащую несколько слов, разделенных пробелами или табуляциями. Например, если в предыдущей программе пользователь введет строку: «Сообщение из нескольких слов», то на экране будет выведено только «Сообщение».
Для ввода и вывода строк в библиотеке stdio.h содержатся специализированные функции gets и puts.
Функция gets предназначена для ввода строк и имеет следующий заголовок:
char * gets(char *buffer);
Между тем использовать функцию gets категорически не рекомендуется, ввиду того, что она не контролирует выход за границу строки, что может произвести к ошибкам. Вместо нее используется функция fgets с тремя параметрами:
char * fgets(char * buffer, int size, FILE * stream);
Функция puts предназначена для вывода строк и имеет следующий заголовок:
int puts(const char *string);
Простейшая программа: ввод и вывод строки с использованием функций fgets и puts будет иметь вид:
Помимо функций ввода и вывода в потоки в библиотеке stdio.h присутствуют функции форматированного ввода и вывода в строки. Функция форматированного ввода из строки имеет следующий заголовок:
Функции форматированного вывода в строку имеют следующие заголовки:
Преобразование строк
В С для преобразования строк, содержащих числа, в численные значения в библиотеке stdlib.h
предусмотрен следующий набор функций:
double atof(const char *string); // преобразование строки в число типа double
int atoi(const char *string); // преобразование строки в число типа int
long int atol(const char *string); // преобразование строки в число типа long int
long long int atoll(const char *string); // преобразование строки в число типа long long int
Корректное представление вещественного числа в текстовой строке должно удовлетворять формату:
После символов E, e указывается порядок числа. Корректное представление целого числа в текстовой строке должно удовлетворять формату:
Помимо приведенных выше функций в библиотеке stdlib.h доступны также следующие функции преобразования строк в вещественные числа:
Аналогичные функции присутствуют и для преобразования строк в целочисленные значения:
Функции обратного преобразования (численные значения в строки) в библиотеке stdlib.h присутствуют, но они не регламентированы стандартом, и рассматриваться не будут. Для преобразования численных значений в строковые наиболее удобно использовать функции sprintf и snprintf.
Обработка строк
В библиотеке string.h содержаться функции для различных действий над строками.
Функция вычисления длины строки:
size_t strlen(const char *string);
Функции копирования строк:
Функции сравнения строк:
Функции осуществляют сравнение строк по алфавиту и возвращают:
положительное значение – если string1 больше string2;
отрицательное значение – если string1 меньше string2;
нулевое значение – если string1 совпадает с string2;
Функции объединения (конкатенации) строк:
Функции поиска символа в строке:
Функция поиска строки в строке:
char * strstr(const char *str, const char *substr);
Функция поиска первого символа в строке из заданного набора символов:
size_t strcspn(const char *str, const char *charset);
Функции поиска первого символа в строке не принадлежащему заданному набору символов:
size_t strspn(const char *str, const char *charset);
Функции поиска первого символа в строке из заданного набора символов:
char * strpbrk(const char *str, const char *charset);
Функция поиска следующего литерала в строке:
char * strtok(char * restrict string, const char * restrict charset);
Строки (Руководство по программированию на C#)
Сравнение строки и System.String
Объявление и инициализация строк
Вы можете объявлять и инициализировать строки различными способами, как показано в следующем примере:
Обратите внимание, что вы не используете оператор new для создания объекта строки, за исключением случаев инициализации строки с помощью массива символов.
Инициализируйте строку с константным значением Empty для создания нового объекта String, строка которого имеет нулевую длину. Представлением строкового литерала строки с нулевой длиной является «». Если вы инициализируете строки со значением Empty вместо NULL, вы снизите вероятность появления исключения NullReferenceException. Используйте статический метод IsNullOrEmpty(String), чтобы проверить значение строки, прежде чем пытаться получить к ней доступ.
Неизменность строковых объектов
Так как «изменение» строки на самом деле является созданием новой строки, создавать ссылки на строки следует с осторожностью. Если вы создадите ссылку на строку, а затем «измените» исходную строку, ссылка будет по-прежнему указывать на исходный объект, а не на новый объект, который был создан при изменении строки. Это поведение проиллюстрировано в следующем коде:
Сведения о создании новых строк, основанных на таких изменениях, как операции поиска и замены исходной строки, см. в инструкциях по изменению содержимого строки.
Регулярные и буквальные строковые литералы
Используйте регулярные строковые литералы, когда вам нужно внедрить escape-символы, доступные в C#, как показано в следующем примере:
Буквальные строковые литералы используются для удобства и читабельности, если текст строки содержит символы обратной косой черты, например в путях к файлам. Так как буквальные строки сохраняют символы новой строки как часть текста строки, их можно использовать для инициализации многострочных строк. Используйте двойные кавычки, чтобы вставить кавычки в буквальной строке. В следующем примере показаны наиболее часто используемым буквальные строки:
Escape-последовательности строк
| Escape-последовательность | Имя символа | Кодировка Юникод |
|---|---|---|
| \’ | Одинарная кавычка | 0x0027 |
| \» | Двойная кавычка | 0x0022 |
| \\ | Обратная косая черта | 0x005C |
| \0 | Null | 0x0000 |
| \a | Предупреждение | 0x0007 |
| \b | Backspace | 0x0008 |
| \f | Перевод страницы | 0x000C |
| \n | Новая строка | 0x000A |
| \r | Возврат каретки | 0x000D |
| \t | Горизонтальная табуляция | 0x0009 |
| \v | Вертикальная табуляция | 0x000B |
| \u | Escape-последовательность Юникода (UTF-16) | \uHHHH (диапазон: 0000–FFFF; пример: \u00E7 = «ç») |
| \U | Escape-последовательность Юникода (UTF-32) | \U00HHHHHH (диапазон: 000000–10FFFF; пример: \U0001F47D = «👽») |
| \x | Escape-последовательность Юникода аналогична «\u», она отличается только длиной переменной | \xH[H][H][H] (диапазон: 0–FFFF; пример: \x00E7 или \x0E7 или \xE7 = «ç») |
Если вы используете escape-последовательность \x с менее чем четырьмя шестнадцатеричными цифрами, то когда непосредственно следующие за ней символы также являются допустимыми шестнадцатеричными цифрами (т. е. 0–9, A–F и a–f), они будут интерпретированы как часть этой escape-последовательности. Например, \xA1 дает результат «¡», являющийся кодовой точкой U+00A1. Однако если следующий символ — «A» или «a», тогда escape-последовательность будет интерпретироваться как \xA1A и даст результат «ਚ», являющийся кодовой точкой U+0A1A. В таких случаях, чтобы избежать некорректной интерпретации, указывайте все четыре шестнадцатеричных знака (например, \x00A1 ).
Во время компиляции буквальные строки преобразуются в обычные строки с теми же escape-последовательностями. Поэтому, если вы просматриваете буквальную строку в окне контрольных значений отладчика, вы увидите escape-символы, добавленные компилятором, а не буквальную версию из исходного кода. Например, буквальная строка @»C:\files.txt» будет отображаться в окне контрольных значений как «C:\\files.txt».
Строки формата
Строка формата — это строка, содержимое которой можно определить динамически во время выполнения. Строки формата создаются путем внедрения интерполированных выражений или заполнителей внутри фигурных скобок в строке. Весь код внутри фигурных скобок ( <. >) будет преобразован в значение и выходные данные как отформатированная строка во время выполнения. Существует два способа создания строк формата: интерполяция строк и составное форматирование.
Интерполяция строк
Начиная с C# 10, можно использовать интерполяцию строк для инициализации константной строки, если все выражения, используемые для заполнителей, также являются константными строками.
Составное форматирование
String.Format использует заполнители в фигурных скобках, чтобы создать строку формата. В этом примере результат аналогичен выходным данным, получаемым с помощью метода интерполяции строк, описанного выше.
Подстроки
Подстрока — это последовательность символов, содержащихся в строке. Используйте метод Substring, чтобы создать новую строку из части исходной строки. Одно вхождение подстроки или несколько можно найти с помощью метода IndexOf. Используйте метод Replace, чтобы заменить все вхождения указанной подстроки новой строкой. Как и метод Substring, метод Replace фактически возвращает новую строку и не изменяет исходную строку. См. дополнительные сведения о поиске строк и изменении содержимого строк.
Доступ к отдельным символам
Используя нотацию массива со значением индекса, можно получить доступ только для чтения к отдельным символам, как показано в следующем примере:
Если вам необходимо изменить отдельные символы в строке и функций методов String вам недостаточно, используйте объект StringBuilder, чтобы изменить отдельные символы «на месте», а затем создайте новую строку для сохранения результатов с помощью методов StringBuilder. В следующем примере предположим, что необходимо определенным образом изменить исходную строку, а затем сохранить результаты для дальнейшего использования:
Строки NULL и пустые строки
Пустая строка — это экземпляр объекта System.String, который содержит нуль символов. Пустые строки часто используются в различных сценариях программирования для представления пустого текстового поля. Вы можете вызывать методы для пустых строк, так как они являются допустимыми объектами System.String. Пустые строки инициализируются следующим образом:
В отличие от пустых строк строка NULL не ссылается на экземпляр объекта System.String, поэтому любая попытка вызвать метод для строки NULL приводит к исключению NullReferenceException. Но вы можете использовать строки NULL в операциях объединения и сравнения с другими строками. В следующих примерах показаны случаи, в которых ссылка на строку NULL вызывает и не вызывает исключение:
Использование класса StringBuilder для быстрого создания строк
В этом примере объект StringBuilder используется для создания строки из набора числовых типов:
Строки, методы расширения и LINQ
В зависимости от языка программирования и используемого точного типа данных переменная, объявленная как строка, может либо вызывать статическое выделение памяти в памяти на заранее определенную максимальную длину, либо использовать динамическое выделение, позволяющее хранить переменное количество элементов.
СОДЕРЖАНИЕ
Строковые типы данных
Длина строки
Кодировка символов
Юникод несколько упростил картину. Большинство языков программирования теперь имеют тип данных для строк Unicode. Предпочтительный формат байтового потока Unicode UTF-8 разработан, чтобы не иметь проблем, описанных выше для более старых многобайтовых кодировок. UTF-8, UTF-16 и UTF-32 требуют, чтобы программист знал, что единицы кода фиксированного размера отличаются от «символов», основная трудность в настоящее время заключается в неверно разработанных API-интерфейсах, которые пытаются скрыть эту разницу (UTF-32 действительно сделать кодовые точки фиксированного размера, но они не являются «символами» из-за составления кодов).
Реализации
Представления
Термин « байтовая строка» обычно указывает на строку байтов общего назначения, а не на строки только (читаемых) символов, строки битов и т. Д. Строки байтов часто подразумевают, что байты могут принимать любое значение, и любые данные могут храниться как есть, что означает, что не должно быть никакого значения, интерпретируемого как значение завершения.
Большинство строковых реализаций очень похожи на массивы переменной длины с записями, хранящими символьные коды соответствующих символов. Принципиальное отличие состоит в том, что при определенных кодировках один логический символ может занимать более одной записи в массиве. Это происходит, например, с UTF-8, где отдельные коды ( кодовые точки UCS ) могут занимать от одного до четырех байтов, а отдельные символы могут принимать произвольное количество кодов. В этих случаях логическая длина строки (количество символов) отличается от физической длины массива (количества используемых байтов). UTF-32 позволяет избежать первой части проблемы.
С нулевым завершением
| F | R | A | N | K | NUL | k | e | f | w |
| 46 16 | 52 16 | 41 16 | 4Э 16 | 4Б 16 | 00 16 | 6Б 16 | 65 16 | 66 16 | 77 16 |
Длина строки «» в приведенном выше примере FRANK составляет 5 символов, но занимает 6 байтов. Знаки после терминатора не являются частью изображения; они могут быть частью других данных или просто мусором. (Строки этой формы иногда называют строками ASCIZ после исходной директивы языка ассемблера, используемой для их объявления.)
Байтовые и битовые оканчивающиеся
В чем-то похожем, машины для «обработки данных», такие как IBM 1401, использовали специальный бит словарной метки для разграничения строк слева, где операция начиналась бы справа. Этот бит должен быть очищен во всех остальных частях строки. Это означало, что, хотя в IBM 1401 было семибитное слово, почти никто никогда не думал использовать его как функцию и отменять назначение седьмого бита (например) для обработки кодов ASCII.
Раннее программное обеспечение для микрокомпьютеров основывалось на том факте, что коды ASCII не используют бит старшего разряда, и устанавливали его для обозначения конца строки. Перед выводом он должен быть сброшен на 0.
С префиксом длины
В последнем случае само поле префикса длины не имеет фиксированной длины, поэтому фактические строковые данные необходимо перемещать, когда строка растет, так что поле длины необходимо увеличивать.
Вот строка Паскаля, хранящаяся в 10-байтовом буфере, вместе с ее представлением ASCII / UTF-8:
| длина | F | R | A | N | K | k | e | f | w |
| 05 16 | 46 16 | 52 16 | 41 16 | 4Э 16 | 4Б 16 | 6Б 16 | 65 16 | 66 16 | 77 16 |
Строки как записи
Многие языки, включая объектно-ориентированные, реализуют строки как записи с внутренней структурой, например:
Другие представления
И завершение символа, и коды длины ограничивают строки: например, символьные массивы C, содержащие нулевые (NUL) символы, не могут обрабатываться напрямую функциями библиотеки строк C : строки, использующие код длины, ограничены максимальным значением кода длины.
Оба эти ограничения можно преодолеть с помощью грамотного программирования.
Хотя эти представления обычны, возможны и другие. Использование веревок делает некоторые строковые операции, такие как вставки, удаления и конкатенации, более эффективными.
Проблемы безопасности
Различная структура памяти и требования к хранению строк могут повлиять на безопасность программы, осуществляющей доступ к строковым данным. Строковые представления, требующие завершающего символа, обычно подвержены проблемам переполнения буфера, если завершающий символ отсутствует, что вызвано ошибкой кодирования или злоумышленником, намеренно изменяющим данные. Строковые представления, использующие отдельное поле длины, также восприимчивы, если длиной можно манипулировать. В таких случаях программный код, обращающийся к строковым данным, требует проверки границ, чтобы гарантировать, что он случайно не получит доступ или не изменит данные за пределами строковой памяти.
Буквальные строки
Иногда строки необходимо встраивать в текстовый файл, который удобен для чтения и предназначен для использования машиной. Это необходимо, например, в исходном коде языков программирования или в файлах конфигурации. В этом случае символ NUL не работает как терминатор, поскольку обычно он невидим (не печатается) и его трудно вводить с клавиатуры. Сохранение длины строки также будет неудобным, поскольку ручное вычисление и отслеживание длины утомительно и подвержено ошибкам.
Два общих представления:
Нетекстовые строки
Алгоритмы обработки строк
Существует множество алгоритмов обработки строк, каждый из которых имеет различные компромиссы. Конкурирующие алгоритмы можно анализировать с точки зрения времени выполнения, требований к хранилищу и т. Д.
Некоторые категории алгоритмов включают:
Название стрингология было придумано в 1984 году компьютерным ученым Цви Галилом для проблемы алгоритмов и структур данных, используемых для обработки строк.
Языки и утилиты, ориентированные на символьные строки
Символьные строки являются настолько полезным типом данных, что было разработано несколько языков, чтобы упростить написание приложений для обработки строк. Примеры включают следующие языки:
Многие утилиты Unix выполняют простые операции со строками и могут использоваться для простого программирования некоторых мощных алгоритмов обработки строк. Файлы и конечные потоки можно рассматривать как строки.
Функции символьных строк
Формальная теория
Конкатенация и подстроки
Префиксы и суффиксы
Разворот
Вращения
Лексикографический порядок
Топология
Строковый тип
В программировании, строковый тип (англ. string «нить, вереница») — тип данных, значениями которого является произвольная последовательность (строка) символов алфавита. Каждая переменная такого типа (строковая переменная) может быть представлена фиксированным количеством байтов либо иметь произвольную длину.
Содержание
Представление в памяти
Некоторые языки программирования накладывают ограничения на максимальную длину строки, но в большинстве языков подобные ограничения отсутствуют. При использовании Unicode каждый символ строкового типа может требовать двух или даже четырёх байтов для своего представления.
Основные проблемы в машинном представлении строкового типа:
В представлении строк в памяти компьютера существует два принципиально разных подхода.
Представление массивом символов
В этом подходе строки представляются массивом символов; при этом размер массива хранится в отдельной (служебной) области. От названия языка Pascal, где этот метод был впервые реализован, данный метод получил название Pascal strings.
Слегка оптимизированным вариантом этого метода является т. н. формат c-addr u (от англ. character-aligned address + unsigned number ), применяемый в Форте. В отличие от Pascal strings, здесь размер массива хранится не совместно со строковыми данными, а является частью указателя на строку.
Преимущества
Недостатки
Метод «завершающего байта»
Второй метод заключается в использовании «завершающего байта». Одно из возможных значений символов алфавита (как правило, это символ с кодом 0) выбирается в качестве признака конца строки, и строка хранится как последовательность байтов от начала до конца. Есть системы, в которых в качестве признака конца строки используется не символ 0, а байт 0xFF (255) или код символа «$».
Метод имеет три названия — ASCIIZ (символы в кодировке ASCII с нулевым завершающим байтом), C-strings (наибольшее распространение метод получил именно в языке Си) и метод нуль-терминированных строк.
Преимущества
Недостатки
Использование обоих методов
В таких языках, как, например, Оберон, строка размещается в массиве символов определённой длины, причём её конец обозначается нулевым символом. По умолчанию, весь массив заполнен нулевыми символами. Такой способ позволяет объединить многие преимущества обоих подходов, а также избежать большинство их недостатков.
Реализация в языках программирования
Операции
Представление символов строки
До последнего времени один символ всегда кодировался одним байтом (8 двоичных битов; применялись также кодировки с 7 битами на символ), что позволяло представлять 256 (128 при семибитной кодировке) возможных значений. Однако для полноценного представления символов алфавитов нескольких языков (многоязыковых документов, типографских символов — несколько видов кавычек, тире, нескольких видов пробелов и для написания текстов на иероглифических языках — китайском, японском и корейском) 256 символов недостаточно. Для решения этой проблемы существует несколько методов:
