Перейти к содержимому

Как задать строку в c

  • автор:

Как задать строку в c

Мы можем работать со строками в С++ в так называемом С-стиле как с массивами символов, которые оканчиваются на нулевой байт ‘0’. Однако, что если такой символ не будет найден или в процессе манипуляций со строкой будет удален, то дальшейшие действия с такой строкой могут иметь недетерминированный результат. По этой причине строки в С-стиле считаются небезопасными, и рекомендуется для хранения строк в C++ использовать тип std::string из модуля .

Объект типа string содержит последовательность символов типа char, которая может быть пустой. Например, определение пустой строки:

std::string message;

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

std::string message ; // или так std::string message2 = "Hello METANIT.COM!"; std::string message3("Hello METANIT.COM!");

В данном случае переменная message получит копию строкового литерала «Hello METANIT.COM!». В своем внутреннем представлении переменная message будет хранить массив символов, который также заканчивается на нулевой байт. Однако реализация типа string и предлагаемые им возможности делают работу с этим типом более безопасной.

И можно инициализировать объект string другим объектом string:

std::string hello; std::string message ; // message = "hello world" // или так // std::string message (hello); // std::string message = hello;

Мы можем вывести подобную строку на консоль:

#include #include int main() < std::string message ; std::cout 

Получение и изменение символов строки

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

std::string hello ; char c ; // e hello[0]='M'; std::cout 

Поскольку объект string представляет последовательность символов, то эту последовательность можно перебрать с помощью цикла for. Например, подсчитаем, сколько раз в строке встречается буква "l":

#include #include int main() < unsigned count<>; // счетчик, сколько раз встречается символ std::string message< "Hello World">; for(const char c: message) < if(c == 'l') < count++; >> std::cout 

Чтение строки с консоли

Для считывания введенной строки с консоли, как и для считывания других значений, можно использовать объект std::cin :

#include #include int main() < std::string name; std::cout > name; std::cout

Input your name: Tom Your name: Tom

Однако если при данном способе ввода строка будет содержать подстроки, разделенные пробелом, то std::cin будет использовать только первую подстроку:

Input your name: Tom Smith Your name: Tom

Чтобы считать всю строку, применяется метод getline() :

#include #include int main()

Метод getline принимает два объекта - std::cin и переменную, в которую надо считать строку.

Input your name: Tom Smith Your name: Tom Smith

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

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

#include #include void main()

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

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

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

#include #include void main()

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

#include #include void main() < char word[10]; word[0] = 'A'; word[1] = 'B'; word[2] = 'C'; word[3] = '\0'; //word[3] = 0; эквивалентно printf("%s", word); getch(); >

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

word[3] = '\0';

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

#include #include void main() < char word[10] = "ABC"; char text[100] = ; printf("%s\n", word); printf("%s", text); getch(); >

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

Чтение строк

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

#include #include void main()

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

#include #include void main() < char buffer[128]; unsigned len = 0; scanf("%127s", buffer); while (buffer[len] != '\0') < len++; >printf("length(%s) == %d", buffer, len); getch(); >

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

while (buffer[len] != 0)
while (buffer[len])

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

#include #include /* Результатом сравнения будет число 0 если слова равны 1 если первое слово больше второго в лексикографическом порядке -1 если второе слово больше */ void main() < char firstWord[128]; //Первое слово char secondWord[128]; //Второе слово 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(); >

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

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Строки и строковые литералы

Строка — это объект типа String, значением которого является текст. Внутри программы текст хранится в виде упорядоченной коллекции объектов Char только для чтения. В конце строки C# нет символа, завершающего значение NULL; поэтому строка C# может содержать любое количество внедренных символов NULL ('\0'). Свойство Length строки соответствует числу содержащихся в ней объектов Char , но не числу символов Юникода. Для доступа к отдельным кодовым точкам Юникода в строке используйте объект StringInfo.

Сравнение строки и System.String

В C# ключевое слово string является псевдонимом для String. Таким образом, String и string эквивалентны, независимо от того, рекомендуется использовать предоставленный псевдоним string , так как он работает даже без using System; . Класс String предоставляет множество методов для безопасного создания, обработки и сравнения строк. Кроме того, язык C# перегружает некоторые операторы для упрощения типичных операций со строками. Дополнительные сведения о ключевых словах см. в статье, посвященной строкам. Дополнительные сведения о типе и его методах см. здесь: String.

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

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

// Declare without initializing. string message1; // Initialize to null. string message2 = null; // Initialize as an empty string. // Use the Empty constant instead of the literal "". string message3 = System.String.Empty; // Initialize with a regular string literal. string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0"; // Initialize with a verbatim string literal. string newPath = @"c:\Program Files\Microsoft Visual Studio 9.0"; // Use System.String if you prefer. System.String greeting = "Hello World!"; // In local variables (i.e. within a method body) // you can use implicit typing. var temp = "I'm still a strongly-typed System.String!"; // Use a const string to prevent 'message4' from // being used to store another string value. const string message4 = "You can't get rid of me!"; // Use the String constructor only when creating // a string from a char*, char[], or sbyte*. See // System.String documentation for details. char[] letters = < 'A', 'B', 'C' >; string alphabet = new string(letters); 

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

Инициализируйте строку с константным значением Empty для создания нового объекта String, строка которого имеет нулевую длину. Представлением строкового литерала строки с нулевой длиной является "". Если вы инициализируете строки со значением Empty вместо NULL, вы снизите вероятность появления исключения NullReferenceException. Используйте статический метод IsNullOrEmpty(String), чтобы проверить значение строки, прежде чем пытаться получить к ней доступ.

Неизменность строк

Строковые объекты неизменяемы: их нельзя изменить после их создания. Может показаться, что все методы String и операторы C# изменяют строку, но в действительности они возвращают результаты в новый строковый объект. Когда содержимое s1 и s2 объединяется для формирования одной строки, две исходные строки не изменяются, как показано в следующем примере. Оператор += создает новую строку, которая содержит объединенное содержимое. Этот новый объект присваивается переменной s1 , а исходный объект, который был присвоен s1 , освобождается для сборки мусора, так как ни одна переменная не ссылается на него.

string s1 = "A string is more "; string s2 = "than the sum of its chars."; // Concatenate s1 and s2. This actually creates a new // string object and stores it in s1, releasing the // reference to the original object. s1 += s2; System.Console.WriteLine(s1); // Output: A string is more than the sum of its chars. 

Так как "изменение" строки на самом деле является созданием новой строки, создавать ссылки на строки следует с осторожностью. Если вы создадите ссылку на строку, а затем "измените" исходную строку, ссылка будет по-прежнему указывать на исходный объект, а не на новый объект, который был создан при изменении строки. Это поведение проиллюстрировано в следующем коде:

string str1 = "Hello "; string str2 = str1; str1 += "World"; System.Console.WriteLine(str2); //Output: Hello 

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

Строковые литералы с кавычками

Строковые литералы в кавычках начинаются и заканчиваются одним символом двойной кавычки ( " ) в одной строке. Строковые литералы с кавычками лучше всего подходят для строк, которые помещаются в одну строку и не включают escape-последовательности. Строковый литерал в кавычках должен внедрять escape-символы, как показано в следующем примере:

string columns = "Column 1\tColumn 2\tColumn 3"; //Output: Column 1 Column 2 Column 3 string rows = "Row 1\r\nRow 2\r\nRow 3"; /* Output: Row 1 Row 2 Row 3 */ string title = "\"The \u00C6olean Harp\", by Samuel Taylor Coleridge"; //Output: "The Æolean Harp", by Samuel Taylor Coleridge 

Строковые литералы verbatim

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

string filePath = @"C:\Users\scoleridge\Documents\"; //Output: C:\Users\scoleridge\Documents\ string text = @"My pensive SARA ! thy soft cheek reclined Thus on mine arm, most soothing sweet it is To sit beside our Cot. "; /* Output: My pensive SARA ! thy soft cheek reclined Thus on mine arm, most soothing sweet it is To sit beside our Cot. */ string quote = @"Her name was ""Sara."""; //Output: Her name was "Sara." 

Необработанные строковые литералы

Начиная с C# 11, можно использовать необработанные строковые литералы для упрощения создания строк, которые являются многостроочными, или использовать любые символы, требующие escape-последовательностей. Необработанные строковые литералы удаляют необходимость использования escape-последовательностей. Вы можете написать строку, включая форматирование пробелов, как она будет отображаться в выходных данных. Необработанный строковый литерал:

  • Начинается и заканчивается последовательность не менее трех символов двойной кавычки ( """ ). Для поддержки строковых литералов, содержащих три повторяющихся символа кавычки, разрешено начинать и заканчивать последовательность более трех последовательных символов.
  • Однострочные необработанные строковые литералы требуют символов открывающей и закрывающей кавычки в одной строке.
  • Многострочный необработанные строковые литералы требуют как открывающих, так и закрывающих символов кавычки в собственной строке.
  • В многострочных строковых литералах все пробелы слева от закрывающих кавычек удаляются.

В следующих примерах демонстрируются следующие правила:

string singleLine = """Friends say "hello" as they pass by."""; string multiLine = """ "Hello World!" is typically the first program someone writes. """; string embeddedXML = """  Here is the main text 
Excerpts from "An amazing story"
"""; // The line "" starts in the first column. // All whitespace left of that column is removed from the string. string rawStringLiteralDelimiter = """" Raw string literals are delimited by a string of at least three double quotes, like this: """ """";

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

// CS8997: Unterminated raw string literal. var multiLineStart = """This is the beginning of a string """; // CS9000: Raw string literal delimiter must be on its own line. var multiLineEnd = """ This is the beginning of a string """; // CS8999: Line does not start with the same whitespace as the closing line // of the raw string literal var noOutdenting = """ A line of text. Trying to outdent the second line. """; 

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

При создании текста, включающего символы, требующие escape-последовательностей при использовании строковых литералов с кавычками или строковых литералов, следует учитывать необработанные строковые литералы. Необработанные строковые литералы будут проще читать, так как они будут более похожи на выходной текст. Например, рассмотрим следующий код, включающий строку форматированного JSON:

string jsonString = """ < "Date": "2019-08-01T00:00:00-07:00", "TemperatureCelsius": 25, "Summary": "Hot", "DatesAvailable": [ "2019-08-01T00:00:00-07:00", "2019-08-02T00:00:00-07:00" ], "TemperatureRanges": < "Cold": < "High": 20, "Low": -10 >, "Hot": < "High": 60, "Low": 20 >>, "SummaryWords": [ "Cool", "Windy", "Humid" ] > """; 

Сравните этот текст с эквивалентным текстом в нашем примере по сериализации JSON, который не использует эту новую функцию.

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-символы, добавленные компилятором, а не буквальную версию из исходного кода. Например, строка verbatim @"C:\files.txt" будет отображаться в окне контрольных значений как "C:\\files.txt".

Строки формата

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

Интерполяция строк

В C# 6.0 и более поздних версий интерполированные строки определяются по специальному символу $ . Они включают интерполированные выражения в фигурных скобках. Если вы не знакомы с интерполяцией строк, ознакомьтесь с интерактивным руководством по C# , чтобы ознакомиться с кратким обзором.

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

var jh = (firstName: "Jupiter", lastName: "Hammon", born: 1711, published: 1761); Console.WriteLine($"  was an African American poet born in ."); Console.WriteLine($"He was first published in at the age of ."); Console.WriteLine($"He'd be over years old today."); // Output: // Jupiter Hammon was an African American poet born in 1711. // He was first published in 1761 at the age of 50. // He'd be over 300 years old today. 

Начиная с C# 10, можно использовать интерполяцию строк для инициализации константной строки, если все выражения, используемые для заполнителей, также являются константными строками.

Начиная с C# 11, можно объединить необработанные строковые литералы с интерполяцией строк. Вы начинаете и заканчиваете строку формата тремя или более последовательными двойными кавычками. Если выходная строка должна содержать < символ или >символ, можно использовать дополнительные $ символы, чтобы указать, сколько > < символов начинается и заканчивается интерполяцией. В выходные данные включается любая последовательность меньшего количества < символов или >символов. В следующем примере показано, как использовать эту функцию для отображения расстояния точки от источника и размещения точки внутри фигурных скобок:

int X = 2; int Y = 3; var pointMessage = $$"""The point >, >> is <> from the origin."""; Console.WriteLine(pointMessage); // Output: // The point is 3.605551275463989 from the origin. 

Составное форматирование

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

var pw = (firstName: "Phillis", lastName: "Wheatley", born: 1753, published: 1773); Console.WriteLine("  was an African American poet born in .", pw.firstName, pw.lastName, pw.born); Console.WriteLine("She was first published in at the age of .", pw.published, pw.published - pw.born); Console.WriteLine("She'd be over years old today.", Math.Round((2018d - pw.born) / 100d) * 100d); // Output: // Phillis Wheatley was an African American poet born in 1753. // She was first published in 1773 at the age of 20. // She'd be over 300 years old today. 

Дополнительные сведения о форматировании типов .NET см. в разделе "Типы форматирования" в .NET.

Подстроки

Подстрока — это последовательность символов, содержащихся в строке. Используйте метод Substring, чтобы создать новую строку из части исходной строки. Одно вхождение подстроки или несколько можно найти с помощью метода IndexOf. Используйте метод Replace, чтобы заменить все вхождения указанной подстроки новой строкой. Как и метод Substring , Replace фактически возвращает новую строку и не изменяет исходную строку. См. дополнительные сведения о поиске строк и изменении содержимого строк.

string s3 = "Visual C# Express"; System.Console.WriteLine(s3.Substring(7, 2)); // Output: "C#" System.Console.WriteLine(s3.Replace("C#", "Basic")); // Output: "Visual Basic Express" // Index values are zero-based int index = s3.IndexOf("C"); // index = 7 

Доступ к отдельным символам

Используя нотацию массива со значением индекса, можно получить доступ только для чтения к отдельным символам, как показано в следующем примере:

string s5 = "Printing backwards"; for (int i = 0; i < s5.Length; i++) < System.Console.Write(s5[s5.Length - i - 1]); >// Output: "sdrawkcab gnitnirP" 

String Если методы не предоставляют функциональные возможности, которые необходимо изменить отдельные символы в строке, можно использовать StringBuilder объект для изменения отдельных символов на месте, а затем создать новую строку для хранения результатов с помощью StringBuilder методов. В следующем примере предположим, что необходимо определенным образом изменить исходную строку, а затем сохранить результаты для дальнейшего использования:

string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?"; System.Text.StringBuilder sb = new System.Text.StringBuilder(question); for (int j = 0; j < sb.Length; j++) < if (System.Char.IsLower(sb[j]) == true) sb[j] = System.Char.ToUpper(sb[j]); else if (System.Char.IsUpper(sb[j]) == true) sb[j] = System.Char.ToLower(sb[j]); >// Store the new string. string corrected = sb.ToString(); System.Console.WriteLine(corrected); // Output: How does Microsoft Word deal with the Caps Lock key? 

Пустые строки и пустые строки

Пустая строка — это экземпляр объекта System.String, который содержит нуль символов. Пустые строки часто используются в различных сценариях программирования для представления пустого текстового поля. Методы можно вызывать для пустых строк, так как они допустимы System.String . Пустые строки инициализируются следующим образом:

string s = String.Empty; 

Напротив, строка NULL не ссылается на экземпляр System.String объекта, и любая попытка вызова метода в строке NULL вызывает исключение NullReferenceException. Но вы можете использовать строки NULL в операциях объединения и сравнения с другими строками. В следующих примерах показаны некоторые случаи, в которых ссылка на пустую строку не вызывает исключение:

string str = "hello"; string nullStr = null; string emptyStr = String.Empty; string tempStr = str + nullStr; // Output of the following line: hello Console.WriteLine(tempStr); bool b = (emptyStr == nullStr); // Output of the following line: False Console.WriteLine(b); // The following line creates a new empty string. string newStr = emptyStr + nullStr; // Null strings and empty strings behave differently. The following // two lines display 0. Console.WriteLine(emptyStr.Length); Console.WriteLine(newStr.Length); // The following line raises a NullReferenceException. //Console.WriteLine(nullStr.Length); // The null character can be displayed and counted, like other chars. string s1 = "\x0" + "abc"; string s2 = "abc" + "\x0"; // Output of the following line: * abc* Console.WriteLine("*" + s1 + "*"); // Output of the following line: *abc * Console.WriteLine("*" + s2 + "*"); // Output of the following line: 4 Console.WriteLine(s2.Length); 

Использование stringBuilder для быстрого создания строк

Строковые операции в .NET оптимизированы и в большинстве случаев не влияют на производительность. Но в некоторых сценариях, например в сплошных циклах, которые выполняются сотни и тысячи раз, операции со строками могут повлиять на производительность. Класс StringBuilder создает строковый буфер, который ускоряет работу, если программа выполняет много операций над строками. Строка StringBuilder также позволяет переназначить отдельные символы, что не поддерживает встроенный строковый тип данных. Например, этот код изменяет содержимое строки без создания новой строки:

System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet"); sb[0] = 'C'; System.Console.WriteLine(sb.ToString()); //Outputs Cat: the ideal pet 

В этом примере объект StringBuilder используется для создания строки из набора числовых типов:

var sb = new StringBuilder(); // Create a string composed of numbers 0 - 9 for (int i = 0; i < 10; i++) < sb.Append(i.ToString()); >Console.WriteLine(sb); // displays 0123456789 // Copy one character of the string (not possible with a System.String) sb[0] = sb[9]; Console.WriteLine(sb); // displays 9123456789 

Строки, методы расширения и LINQ

Так как тип String использует IEnumerable , вы можете применять методы расширения, определенные для строк в классе Enumerable. Чтобы избежать визуального загромождений, эти методы исключаются из IntelliSense для String типа, но они доступны тем не менее. Можно также использовать выражения запроса LINQ в строках. Дополнительные сведения см. в документации по LINQ и строкам.

Похожие статьи

  • Изменение содержимого строк. Иллюстрирует методы преобразования строк и изменения содержимого строк.
  • Сравнение строк. Показывает, как выполнять порядковые и региональные параметры для сравнения строк.
  • Объединение нескольких строк. Демонстрирует различные способы объединения нескольких строк в одну.
  • Анализ строк с помощью String.Split: содержит примеры кода, иллюстрирующие использование String.Split метода для синтаксического анализа строк.
  • Практическое руководство. Описание использования поиска определенного текста или шаблонов в строках.
  • Как определить, представляет ли строка числовое значение: показывает, как безопасно проанализировать строку, чтобы узнать, имеет ли она допустимое числовое значение.
  • Интерполяция строк. Описывает функцию интерполяции строк, которая предоставляет удобный синтаксис для форматирования строк.
  • Основные операции со строками. Содержит ссылки на статьи, которые используют System.String и System.Text.StringBuilder методы для выполнения базовых строковых операций.
  • Анализ строк. Описывает преобразование строковых представлений базовых типов .NET в экземпляры соответствующих типов.
  • Анализ строк даты и времени в .NET. Показывает, как преобразовать строку, например "01.24.2008" в System.DateTime объект.
  • Сравнение строк. Включает сведения о том, как сравнивать строки и предоставляет примеры в C# и Visual Basic.
  • Использование класса StringBuilder. Описывает создание и изменение динамических строковых объектов с помощью StringBuilder класса.
  • LINQ и строки. Предоставляет сведения о выполнении различных строковых операций с помощью запросов LINQ.

Си-строки (массивы символов)

В прошлом уроке мы разобрали динамические String-строки в реализации Arduino, а сейчас настало время стандартных статических строк языка C/C++. Такая строка представляет собой массив символов типа char (char array) и для неё работает такой же синтаксис, как и для остальных массивов (урок про массивы). Конец строки определяется нулевым символом \0 (или целым число 0 ), за это такой тип строк называют null-terminated string: ноль на конце позволяет программе определять конец строки и её длину. Также это стандартные строки языка Си и поэтому называются cstring.

Текст в кавычках

Любой написанный в двойных кавычках текст "some text" :

  • Является строковой константойstring constant
  • Имеет тип данных const char* – то есть указывает на свой первый символ в памяти
  • Хранится и в программной, и в оперативной памяти микроконтроллера
  • Компилятор автоматически добавляет нулевой символ в конец строки '\0' – то есть реальный размер строки всегда на 1 символ больше
  • Оптимизируется компилятором – об этом ниже

Оптимизация компилятором

Компилятор оптимизирует строковые константы, но не во всех случаях. Если создать несколько строк как массивы (которые можно изменять) и присвоить им одинаковые строки, то они займут место в памяти как разные строки, т.е. столько, сколько в них суммарно символов:

char s1[] = "hello"; char s2[] = "hello";

Если создать несколько одинаковых строк как указатели – то компилятор их оптимизирует и они займут место в памяти как одна строка!

const char* s1 = "hello"; const char* s2 = "hello";

Если при выводе в Serial или передаче в другие функции мы используем одинаковые строки, то они также будут оптимизироваться и занимать место как одна строка:

Serial.println("hello"); lcd.print("hello"); String s("hello");

В то же время F() – строки (подробнее в уроке про PROGMEM) не оптимизируются компилятором и занимают в программной памяти каждая своё место:

Serial.println(F("hello")); lcd.print(F("hello"));

Сложение

Строковые константы можно складывать через пробелы:

char str[] = "Hello" ", " "World!";

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

Перенос строк (в программе)

Длинную строку можно переносить для удобства чтения и редактирования программы. Есть два способа:

Первый способ – работает как сложение строк в предыдущей главе. Каждая строка в своих кавычках пишется с новой строки:

char str[] = "Hello" ", " "World!";

Второй способ – использование символа обратный слэш \ для переноса строки. Кавычки в этом случае нужны только в начале и конце:

char str[] = "Hello\ , \ World!";

Примечание: результирующий текст в переменной str в обоих случаях не имеет переносов, то есть в обоих примерах получится строка "Hello, World!" .

Перенос строк (текст)

Для человека текст с новой строки – это текст с новой строки. Чтобы перенести текст на новую строку, мы нажимаем на клавишу Enter на клавиатуре. В то же время текст в текстовых файлах не хранится в разных “строках”, он лежит в памяти одной длинной строкой. Когда мы открываем файл, компьютер читает текст и ищет в нём специальные невидимые символы, которые называются управляющими символами. Одним из таких символов является перенос строки – \n , именно его добавляет клавиша Enter. Чтобы компьютер при выводе строки перенёс её – нужно добавить этот символ в текст. В программе мы будем видеть этот символ, а вот в результирующем тексте он автоматически превратится в перенос строки. Примеры (без переноса в программе и с переносом двумя способами):

char str1[] = "Строка1\nСтрока2\nСтрока3"; char str2[] = "Строка1\ \nСтрока2\ \nСтрока3"; char str3[] = "Строка1\n" "Строка2\n" "Строка3";

Во всех трёх случаях получится текст

Строка1 Строка2 Строка3

Примечание: ставить символ переноса строки можно как в начале новой строки (см. str2 ), так и в конце предыдущей (см. str3 ).

Кавычки внутри строки, экранирование

На практике довольно часто бывает нужно иметь строку, которая содержит символы двойных кавычек, например для вёрстки html:

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

// приведёт к ошибке компиляции char str1[] = "Символ " кавычка"; // приведёт к ошибке компиляции char str2[] = "Этот "текст" в кавычках"; // скомпилируется, получится Этот текст в кавычках (сложение трёх строк) char str3[] = "Этот ""текст"" в кавычках";

Эту проблему можно решить двумя способами: экранированием и использованием инструмента компилятора raw string literal (С++ 11). Экранирование кавычек во многих языках программирования осуществляется при помощи обратного слэша \ . Таким образом просто кавычки " – это оператор, часть синтаксиса языка, а вот так – \" – это печатный символ кавычек, который может входить в состав строки:

// скомпилируется, получится Этот "текст" в кавычках char str4[] = "Этот \"текст\" в кавычках";

“Сырые” строки

“Сырые” строки – очень удобный инструмент компилятора, позволяющий задать любой текст просто в виде текста, включая кавычки и переносы строк без дополнительного экранирования. Синтаксис следующий R"(ваш текст)" или R"метка(ваш текст)метка" , где метка – любой текст длиной до 16 символов без пробелов, должен быть одинаковым в начале и конце. Нужна для того, чтобы компилятор мог корректно определить конец сырой строки, если внутри самой строки есть )" . Например строка R"()" приведёт к ошибке, т.к. компилятор решит что она закончилась после слова func ! Добавим метку R"raw()raw" и компилятор без ошибки найдёт конец сырой вставки. Примеры:

// вывод: текст "с кавычками" - удобно Serial.println(R"(текст "с кавычками" - удобно)"); char str1[] = R"(текст с переносами строки)"; Serial.println(str1); // метку rawliteral часто можно встретить в примерах для esp8266/32. char str2[] = R"rawliteral( )rawliteral"; Serial.println(str2);

Примечание: перенос строки внутри экранированной строки в программе станет переносом строки в итоговой строке в переменной!

Массив символов

Объявление как массив

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

char str[] = ; // с нулевым символом на конце

Такой вариант записи не очень удобный, поэтому строки в C/C++ можно задавать просто текстом в двойных кавычках – компилятор сам посчитает размер массива:

char str[] = "hello";

Полученный выше массив содержит 6 символов: 5 на слово hello и 1 на завершающий символ. Текст в данном массиве можно изменять в процессе работы программы, потому что с точки зрения программы мы создали обычный массив и заполнили его буквами. Изменим первую букву на прописную: str[0] = 'H'; . Выведем в монитор порта:

Serial.println(str);

Serial умеет работать с такими данными и с радостью их выведет.

Объявление как указатель

Также строку можно объявить как указатель на const char* – то есть сам текст в кавычках хранится где то в программе, а мы получаем на него “ссылку”:

const char* str = "hello";

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

Serial.println(str);

Примечание: можно объявить и как char* str = "hello"; и пользоваться дальше точно так же как массивом, но компилятор выдаст предупреждение что строковая константа (текст в кавычках) приравнивается к неконстантному типу.

Массив строк

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

// объявляем массив строк const char* names[] = < "Period", // 0 "Work", // 1 "Stop", // 2 >; // выводим третий элемент Serial.println(names[2]); // выведет Stop

Таким образом удобно паковать строки для создания текстовых меню и прочего. Единственный большой минус – весь этот текст висит в оперативной памяти мёртвым грузом. Можно сохранить его во Flash – программной памяти (PROGMEM), об этом читайте в отдельном уроке.

Точно также можно создать массив массив пустых строк для дальнейшей работы:

char arr[к-во строк][макс. длина];

По сути это будет двухмерный массив. Копирование другой строки в массив может выглядеть так: strcpy(arr[0], str); . Об этом читайте ниже.

Длина строки

Для определения длины текста можно использовать оператор strlen() , который возвращает количество символов в строке. Сравним его работу с оператором sizeof() :

char str[100] = "World"; sizeof(str); // вернёт 100 strlen(str); // вернёт 5

Здесь оператор sizeof() вернул количество байт, занимаемое массивом. Массив я специально объявил с размером бОльшим, чем содержащийся в нём текст. А вот оператор strlen() посчитал и вернул количество символов, которые идут с начала массива и до нулевого символа в конце текста без его учёта. А вот такой будет результат при инициализации без указания размера массива:

char text[] = "Hello"; strlen(text); // вернёт 5 ("читаемых" символов) sizeof(text); // вернёт 6 (байт)

Отличия от String

В отличие от String-строк, Си-строки:

char str[] = "hello"; char str2[] = "world"; str += str2; // НЕЛЬЗЯ складывать str = "text"; // НЕЛЬЗЯ присваивать после инициализации if (str == str2); // НЕЛЬЗЯ сравнивать

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

Оптимизация памяти

Как я писал выше – “текст в кавычках” хранится и в памяти программы, и в оперативной памяти, то есть после запуска микроконтроллера строка загружается в оперативную память, и уже там мы имеем к ней доступ. Как правило, объём программной памяти микроконтроллера в несколько раз больше, чем оперативной. Есть несколько возможностей хранения строк только в программной памяти, об этом очень подробно поговорим в уроке про PROGMEM.

Инструменты для Си-строк

Массивы символов не так просты, как кажутся: их возможности сильно расширяет стандартная библиотека cstring. Использование всех доступных фишек по работе с массивами символов позволяет полностью избавить свой код от тяжёлых String-строк и сделать его легче, быстрее и оптимальнее. Подробно обо всех инструментах можно почитать в официальной документации. Очень интересный пример с манипуляцией этими инструментами можно посмотреть здесь. А мы вкратце рассмотрим самые полезные.

Конвертирование

Есть готовые функции, позволяющие конвертировать различные типы данных в строки:

  • itoa(int_data, str, base) – записывает переменную типа int int_data в строку str с базисом* base.
  • utoa(uint_data, str, base) – записывает переменную типа unsigned int uint_data в строку str с базисом* base.
  • ltoa (long_data, str, base) – записывает переменную типа long long_data в строку str с базисом* base.
  • ultoa (unsigned_long_data, str, base) – записывает переменную типа unsigned long unsigned_long_data в строку str с базисом* base.
  • dtostrf(float_data, width, dec, str) – записывает переменную типа float float_data в строку str с количеством символов width и знаков после запятой dec.

* Примечание: base – основание системы счисления, тут всё как при выводе в Serial:

  • DEC – десятичная
  • BIN – двоичная
  • OCT – восьмеричная
  • HEX – шестнадцатеричная
float x = 12.123; char str[10] = ""; dtostrf(x, 4, 2, str); // тут str == "12.12" int y = 123; itoa(y, str, DEC); // тут str == "123"

И наоборот, можно преобразовывать строки в численные данные, функция вернёт результат:

  • atoi(str) – преобразование str в int
  • atol(str) – преобразование str в long
  • atof(str) – преобразование str в float
float x; char str[10] = "12.345"; x = atof(str); // тут x == "12.345"

Внимание! Функции конвертирования, работающие с типом float, являются очень тяжёлыми: их “подключение” занимает ~2 кБ Flash памяти!! Максимально избегайте их применения в крупном проекте. Для преобразования можно сделать свою функцию, практически готовые варианты для всех типов данных можно найти в стандартной ардуиновской Print.cpp (ссылка на файл на гитхабе Arduino).

Работа с байтовым буфером

Очень часто в реальных задачах встречается ситуация, когда текстовые данные приходят в виде массива byte : по какому-нибудь каналу связи (MQTT, UDP, Bluetooth…), при чтении из файлов и так далее. Например приём по MQTT во многих библиотеках выглядит так:

void callback(byte* payload, uint16_t len)

Пришёл поток байтов известной длины. Что с ними делать, если это текст и нам в программе он нужен как строка? Во многих примерах в Интернете предлагают преобразовать данные в String , просто как String s = (char*)payload . Делать так категорически нельзя, если переданный текст не оканчивается нулевым символом, а в большинстве случаев это как раз так. Дело в том, что свободная оперативная память во время работы микроконтроллера содержит не нули, а фактически случайные значения, оставшиеся от выгруженных переменных в разных местах программы. И если у нас приходит массив, который не оканчивается нулём, то в памяти после него тоже не обязан быть ноль, и при преобразовании в строку пойдёт вся память по порядку, пока не встретится ноль. Простой пример:

char str0[] = ; char str1[] = ; char str2[] = ; Serial.println(str0); // abc Serial.println(str1); // defabc Serial.println(str2); // ghidefabc

Отсюда видно, что в строку пойдут любые данные из памяти, пока не встретится ноль. Что делать? Варианта два.

Через String

Если нужна String-строка, то нужно её создать, зарезервировать место под текст (чтобы избежать лишних аллокаций) и переписать в неё данные. К сожалению в реализации Arduino функция для переписывания массива в строку сделана приватной, поэтому придётся просто прибавить данные в цикле. Этот способ делает в два раза больше действий, чем могло бы быть, но для String-строки это единственный способ:

void callback(byte* payload, uint16_t len)

Через cstring

Здесь алгоритм будет такой: создать массив char с запасом под нулевой символ, переписать в него данные и нулевой символ в конце:

void callback(byte* payload, uint16_t len)

Данный способ сильно быстрее и эффективнее чем String. Дальше можно работать с созданной строкой как обычно.

Прочее

Инструменты для копирования, поиска и сравнения

strcpy(str1, str2)

Копирует str2 в str1, включая NULL . Так как мы передаём указатель, цель и место назначения можно “подвинуть”:

char str1[] = "hello world"; char str2[] = "goodbye"; // вставим bye после hello strcpy(str1 + 6, str2 + 4); // тут str1 == hello bye

strncpy(str1, str2, num)

Копирует num символов из начала str2 в начало str1

char str1[] = "hello world"; char str2[] = "goodbye"; // вставим good после hello strncpy(str1 + 6, str2, 4); // тут str1 == hello goodd // вторая d осталась после "world"

strcat(str1, str2)

Прибавляет str2 к str1, при этом str1 должна иметь достаточный для этого размер. NULL первой строки заменяется на первый символ из str2

char str1[15] = "hello "; char str2[] = "world"; strcat(str1, str2); // здесь str1 - "hello world"

strncat(str1, str2, num)
Добавляет num символов из начала str2 к концу str1
strcmp(str1, str2)

Сравнивает str1 и str2. Возвращает 0, если строки одинаковы. Больше нуля, если str1 > str2. Меньше нуля, если str1 < str2.

strncmp(str1, str2, num)
Сравнивает первые num символов из строк str1 и str2. Возвращает 0, если эти участки одинаковы.
strchr(str, symb)
Ищет символ symb в строке str и возвращает указатель на первое совпадение.
strrchr(str, symb)
Ищет символ symb в строке str и возвращает указатель на последнее совпадение.
strcspn(str1, str2)

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

strpbrk(str1, str2)

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

strspn(str1, str2)

Поиск символов строки str2 в строке str1. Возвращает длину начального участка строки str1, который состоит только из символов строки str2.

strstr(str1, str2)
Функция ищет первое вхождение подстроки str2 в строке str1.
strtok(str, delim)

Ищет символы-разделители delim в строке str, возвращает указатель на последний найденный. Как использовать – смотри тут.

Возвращает длину строки str без учёта нулевого символа.

Дублирует указанную str строку, динамически выделяя память под новую строку, возвращает указатель на новую строку. Внимание! Новая строка будет в динамической памяти, чтобы удалить такую строку – нужно использовать оператор delete или free .

Библиотека

У меня есть библиотека для удобной работы с Си-строками, по возможностям схожая со String, но гораздо легче и эффективнее. Библиотека называется mString, документацию и примеры смотрите на GitHub.

Видео

Полезные страницы

  • Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
  • Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
  • Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
  • Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
  • Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
  • Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
  • Поддержать автора за работу над уроками
  • Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *