В одномерном массиве, элементы которого - целые числа, произвести следующие действия:
Удалить из массива все четные числа.
Вставить новый элемент после всех элементов, которые заканчиваются на данную цифру.
Удалить из массива повторяющиеся элементы, оставив только их первые вхождения.
Вставить новый элемент между всеми парами элементов, имеющими разные знаки.
Уплотнить массив, удалив из него все нулевые значения.
В двумерном массиве, элементы которого - целые числа, произвести следующие действия:
Вставить новую строку после строки, в которой находится первый встреченный минимальный элемент.
Вставить новый столбец перед всеми столбцами, в которых встречается заданное число.
Удалить все строки, в которых нет ни одного четного элемента.
Удалить все столбцы, в которых все элементы положительны.
Удалить из массива k-тую строку и j-тый столбец, если их значения совпадают.
Уплотнить массив, удалив из него все нулевые строки и столбцы
Лекция 9. Символы и строки
Обработка текстовой информации является одной из самых распространенных задач современного программировании. С# предоставляет для ее решения широкий набор средств: символы char, неизменяемые строки string, изменяемые строки StringBuider и регулярные выражения Regex. В данном разделе мы рассмотрим работу с символами, неизменяемыми и изменяемыми строками.
9.1 Символы char
Символьный тип char предназначен для хранения символа в кодировке Unicode. Символьный тип относится к встроенным типам данных С# и соответствует стандартному классу Сhar библиотеки .Net из пространства имен System. В этом классе определены статические методы, позволяющие задавать вид и категорию символа, а также преобразовывать символ в верхний или нижний регистр, в число. Рассмотрим основные методы:
Метод
Описание
GetNumericValue
Возвращает числовое значение символа, если он является цифрой, и -1 в противном случае.
GetUnicodeCategory
Возвращает категорию Unicode-символа. В Unicode символы разделены на категории, например цифры (DecimalDigitNumber), римские цифры (LetterNumber), разделители строк (LineSeparator), буквы в нижнем регистре (LowercaseLetter) и т.д.
IsControl
Возвращает true, если символ является управляющим.
IsDigit
Возвращает true, если символ является десятичной цифрой.
IsLetter
Возвращает true, если символ является буквой.
IsLetterOrDigit
Возвращает true, если символ является буквой или десятичной цифрой.
IsLower
Возвращает true, если символ задан в нижнем регистре.
IsNumber
Возвращает true, если символ является числом (десятичным или шестнадцатеричным).
IsPunctuation
Возвращает true, если символ является знаком препинания.
IsSeparator
Возвращает true, если символ является разделителем.
IsUpper
Возвращает true, если символ задан в верхнем регистре.
IsWhiteSpace
Возвращает true, если символ является пробельным (пробел, перевод строки, возврат каретки).
Parse
Преобразует строку в символ (строка должна состоять из одного символа).
ToLower
Преобразует символ в нижний регистр
ToUpper
Преобразует символ в верхний регистр
В следующем примере рассмотрим применение данных методов:
static void Main(){ try { char b = 'B', c = '\x64', d = '\uffff'; Console.WriteLine("{0}, {1}, {2}", b, c, d); Console.WriteLine("{0}, {1}, {2}", char.ToLower(b), char.ToUpper(c), char.GetNumericValue(d)); char a; do //цикл выполнятеся до тех пор, пока не ввели символ e { Console.WriteLine("Введите символ: "); a = char.Parse(Console.ReadLine()); Console.WriteLine("Введен символ {0}, его код {1}, его категория {2}", a, (int)a, char.GetUnicodeCategory(a)); if (char.IsLetter(a)) Console.WriteLine("Буква"); if (char.IsUpper(a)) Console.WriteLine("Верхний регистр"); if (char.IsLower(a)) Console.WriteLine("Нижний регистр"); if (char.IsControl(a)) Console.WriteLine("Управляющий символ"); if (char.IsNumber(a)) Console.WriteLine("Число"); if (char.IsPunctuation(a)) Console.WriteLine("Разделитель"); } while (a != 'e'); } catch { Console.WriteLine("Возникло исключение"); }}
Используя символьный тип можно оргранизовать массив символов и работать с ним на основе базового класса Array:
static void Main(){ char[] a ={ 'm', 'a', 'Х', 'i', 'M', 'u', 'S' , '!', '!', '!' }; char [] b="кол около колокола".ToCharArray(); //преобразование строки в массив символов PrintArray("Исходный массив а:", a); for (int x=0;x<a.Length; x++) if (char.IsLower(a[x])) a[x]=char.ToUpper(a[x]); PrintArray("Измененный массив а:", a); PrintArray("Исходный массив b:", b); Array.Reverse(b); PrintArray("Измененный массив b:", b);} static void PrintArray(string line, Array a){ Console.WriteLine(line); foreach( object x in a) Console.Write(x); Console.WriteLine('\n');}
Задание. Измените программу так, чтобы в ней подсчитывалось количество знаков пунктуации в массиве a.
9.2 Неизменяемые строки string
Тип string, предназначенный для работы со строками символов в кодировке Unicode, является встроенным типом С#. Ему соответствует базовый тип класса System.String библиотеки .Net. Каждый объект string - это неизменяемая последовательность символов Unicode, т.е. методы, предназначенные для изменения строк, возвращают измененные копии, исходные же строки остаются неизменными.
Создать строку можно несколькими способами:
1. string s; // инициализация отложена2. string s=''кол около колокола''; //инициализация строковым литералом3. string s=@'Привет!' //символ @ сообщает конструктору string, что строку 4. Сегодня хорошая погода!!! '' // нужно воспринимать буквально, даже если она занимает 5. //несколько строк
7. string s=new string (' ', 20); //конструктор создает строку из 20 пробелов 8. int x = 12344556; //инициализировали целочисленную переменную9. string s = x.ToString(); //преобразовали ее к типу string
11. char [] a={'a', 'b', 'c', 'd', 'e'}; //создали массив символов12. string v=new string (a); // создание строки из массива символов
14. char [] a={'a', 'b', 'c', 'd', 'e'};15. // создание строки из части массива символов, при этом: 0 16. string v=new string (a, 0, 2) 17. // показывает с какого символа, 2 - сколько символов 18. // использовать для инициализации19.
Класс string обладает богатым набором методов для сравнения строк, поиска в строке и других действий со строками. Рассмотрим эти методы.
Название
Вид
Описание
Compare
Статический метод
Сравнение двух строк в лексикографическом (алфавитном) порядке. Разные реализации метода позволяют сравнивать строки с учетом или без учета регистра.
CompareTo
Метод
Сравнение текущего экземпляра строки с другой строкой.
Форматирование строки в соответствии с заданным форматом
IndexOf, IndexOfAny, LastIndexOf, LastIndexOfAny
Экземплярные методы
Определение индексов первого и последнего вхождения заданной подстроки или любого символа из заданного набора в данную строку.
Insert
Экземплярный метод
Вставка подстроки в заданную позицию
Join
Статический метод
Слияние массива строк в единую строку. Между элементами массива вставляются разделители.
Length
Свойство
Возвращает длину строки
PadLeft, PadRigth
Экземплярные методы
Выравнивают строки по левому или правому краю путем вставки нужного числа пробелов в начале или в конце строки.
Remove
Экземплярный метод
Удаление подстроки из заданной позиции
Replace
Экземплярный метод
Замена всех вхождений заданной подстроки или символа новыми подстрокой или символом.
Split
Экземплярный метод
Разделяет строку на элементы, используя разные разделители. Результаты помещаются в массив строк.
StartWith, EndWith
Экземплярные методы
Возвращают true или false в зависимости от того, начинается или заканчивается строка заданной подстрокой.
Substring
Экземплярный метод
Выделение подстроки, начиная с заданной позиции
ToCharArray
Экземплярный метод
Преобразует строку в массив символов
ToLower, ToUpper
Экземплярные методы
Преобразование строки к нижнему или верхнему регистру
Trim, TrimStart, TrimEnd
Экземплярные методы
Удаление пробелов в начале и конце строки или только с одного ее конца.
Напоминаем, что вызов статических методов происходит через обращение к имени класса, например, String.Concat(str1, str2), в остальных случаях через обращение к экземплярам класса, например, str.ToLower(). На примере рассмотрим использование данных свойств и методов.
static void Main(){ string str1 ="Первая строка"; string str2 = string.Copy(str1); string str3 = "Вторая строка"; string str4 = "ВТОРАЯ строка"; string strUp, strLow; int result, idx; Console.WriteLine("str1: " + str1); Console.WriteLine("Длина строки str1: " +str1.Length); // Создаем прописную и строчную версии строки str1. strLow = str1.ToLower(); strUp = str1.ToUpper(); Console.WriteLine("Строчная версия строки str1: " +strLow); Console.WriteLine("Прописная версия строки str1: " +strUp); Console.WriteLine(); // Сравниваем строки, result = str1.CompareTo(str3); if (result == 0) Console.WriteLine("str1 и str3 равны."); else if (result < 0) Console.WriteLine("str1 меньше, чем str3"); else Console.WriteLine("str1 больше, чем str3"); Console.WriteLine(); //сравниваем строки без учета регистра result = String.Compare(str3,str4,true); if (result == 0) Console.WriteLine("str3 и str4 равны без учета регистра."); else Console.WriteLine("str3 и str4 не равны без учета регистра."); Console.WriteLine(); //сравниваем части строк result = String.Compare(str1, 4, str2, 4, 2); if (result == 0) Console.WriteLine("часть str1 и str2 равны"); else Console.WriteLine("часть str1 и str2 не равны"); Console.WriteLine(); // Поиск строк. idx = str2.IndexOf("строка"); Console.WriteLine("Индекс первого вхождения подстроки сторка: " + idx); idx = str2.LastIndexOf("о"); Console.WriteLine("Индекс последнего вхождения символа о: " + idx); //конкатенация string str=String.Concat(str1, str2, str3, str4); Console.WriteLine(str); //удаление подстроки str=str.Remove(0,str1.Length); Console.WriteLine(str); //замена подстроки "строка" на пустую подстроку str=str.Replace("строка",""); Console.WriteLine(str);}
Очень важными методами обработки строк, являются методы разделения строки на элементы Split и слияние массива строк в единую строку Join.
static void Main(){ string poems = "тучки небесные вечные странники"; char[] div = { ' '}; //создаем массив разделителей // Разбиваем строку на части, string[] parts = poems.Split(div); Console.WriteLine("Результат разбиения строки на части: "); for (int i = 0; i < parts.Length; i++) Console.WriteLine(parts[i]); // Теперь собираем эти части в одну строку, в качестве разделителя используем символ | string whole = String.Join(" | ", parts); Console.WriteLine("Результат сборки: "); Console.WriteLine(whole);}
Задание. Измените программу так, чтобы слова в предложении записывались в обратном порядке.
В общем случае строка может содержать и другие разделители:
static void Main(){ string poems = "Тучки небесные, вечные странники..."; char[] div = { ' ', ',', '.'}; //создаем массив разделителей // Разбиваем строку на части, string[] parts = poems.Split(div); Console.WriteLine("Результат разбиения строки на части: "); for (int i = 0; i < parts.Length; i++) Console.WriteLine(parts[i]); // Теперь собираем эти части в одну строку, string whole = String.Join(" | ", parts); Console.WriteLine("Результат сборки: "); Console.WriteLine(whole);}
Задания.
Объясните, почему в массиве строк parts появились пустые строки.
Внесите изменения в программу так, чтобы пустых строк не было.
Рассмотрим другой пример - используя метод Split вводить двумерный массив можно не поэлементно, а построчно:
static void Main(){ try { int[][] MyArray; Console.Write("введите количество строк: "); int n = int.Parse(Console.ReadLine()); MyArray = new int[n][]; for (int i = 0; i < MyArray.Length; i++) { string line = Console.ReadLine(); string[] mas = line.Split(' '); MyArray[i] = new int[mas.Length]; for (int j = 0; j < MyArray[i].Length; j++) { MyArray[i][j] = int.Parse(mas[j]); } } PrintArray("исходный массив:", MyArray); for (int i = 0; i < MyArray.Length; i++) Array.Sort(MyArray[i]); PrintArray("итоговый массив", MyArray); } catch { Console.WriteLine("возникло исключение"); }} static void PrintArray(string a, int[][] mas){ Console.WriteLine(a); for (int i = 0; i < mas.Length; i++) { foreach (int x in mas[i]) Console.Write("{0} ", x); Console.WriteLine(); }}
В этом примере могут возникнуть исключительные ситуации, если введенная строка элементов массива будет содержать лишние пробелы. Следовательно, от этих пробелов нужно избавиться:
static void Main(){ try { int[][] MyArray; Console.Write("введите количество строк: "); string line= Console.ReadLine() int n = int.Parse(line.Trim()); MyArray = new int[n][]; for (int i = 0; i < MyArray.Length; i++) { line = Console.ReadLine(); line=line.Trim(); //удалаяем пробелы в начале и конце строки //удаляем линшие пробелы внутри строки n = line.IndexOf(" "); while (n > 0) { line = line.Remove(n, 1); n = line.IndexOf(" "); } string[] mas = line.Split(' '); MyArray[i] = new int[mas.Length]; for (int j = 0; j < MyArray[i].Length; j++) { MyArray[i][j] = int.Parse(mas[j]); } } PrintArray("исходный массив:", MyArray); for (int i = 0; i < MyArray.Length; i++) Array.Sort(MyArray[i]); PrintArray("итоговый массив", MyArray); } catch { Console.WriteLine("возникло исключение"); }} static void PrintArray(string a, int[][] mas){ Console.WriteLine(a); for (int i = 0; i < mas.Length; i++) { foreach (int x in mas[i]) Console.Write("{0} ", x); Console.WriteLine(); }}
Задание. Объясните, можно ли удалить внутри строки лишние пробелы, используя метод Replace. Например, следующим способом str.Replace(" ", " "), где мы пытаемся заменить подстроку состоящую из двух, на подстроку из одиного пробела.
При работе с объектами класса string нужно учитывать их свойство неизменяемости, т.е. тот факт, что методы изменяют не сами строки, а их копии. Рассмотрим фрагмент программы:
string a=""; for (int i = 1; i <= 100; i++) a +="!"; Console.WriteLine(a);
В этом случае в памяти компьютера будет сформировано 100 различных строк вида:
!!!!!!…!!!...!!
И только последняя строка будет храниться в переменной а. Ссылки на все остальные строчки будут потеряны, но эти строки будут храниться в памяти компьютера и засорять память. Боротся с таким засорением придется сборщику мусора, что будет сказываться на производительности программы. Поэтому если нужно изменять строку, то лучше пользоваться классом StringBuilder.
9.3 Изменяемые строки
Чтобы создать строку, которую можно изменять, в С# предусмотрен класс StringBuilder, определенный в пространстве имен System.Text. Объекты этого класса всегда объявляются с явным вызовом конструктора класса (через операцию new) . Примеры создания изменяемых строк:
StringBuilder a =new StringBuilder(); //создание пустой строки, размер по умолчанию 16 символов//инициализация строки и выделение необходимой памятиStringBuilder b = new StringBuilder("abcd"); //создание пустой строки и выделение памяти под 100 символовStringBuilder с = new StringBuilder(100);//инициализация строки и выделение памяти под 100 символов StringBuilder d = new StringBuilder("abcd", 100);//инициализация подстрокой "bcd", и выделение памяти под 100 символов StringBuilder d = new StringBuilder("abcd", 1, 3,100);
Основные элементы класса приведены в таблице:
Название
Вид
Описание
Append
Экземплярный метод
Добавление данных в конец строки. Разные варианты метода позволяют добавлять в строку величины любых встроенных типов, массивы символов, строки и подстроки string.
AppendFormat
Экземплярный метод
Добавление форматированной строки в конец строки
Capacity
свойство
Получение и установка емкости буфера. Если устанавливаемое значение меньше текущей длины строки или больше максимального, то генерируется исключение ArgumentOutOfRangeException
Insert
Экземплярный метод
Вставка подстроки в заданную позицию
Length
изменяемое свойство
Возвращает длину строки. Присвоение ему значения 0 сбрасывает содержимое и очищает строку
MaxCapacity
неизменное свойство
Возвращает наибольшее количество символов, которое может быть размещено в строке
Remove
Экземплярный метод
Удаление подстроки из заданной позиции
Replace
Экземплярный метод
Замена всех вхождений заданной подстроки или символа новой подстрокой или символом
ToString
Экземплярный метод
Преобразование в строку типа string
Chars
изменяемое свойство
Возвращает из массива или устанавливает в массиве символ с заданным индексом. Вместо него можно пользоваться квадратными скобками []
Equals
Экземплярный метод
Возвращает true, только если объекты имеют одну и ту же длину и состоят из одних и тех же символов
CopyTo
Экземплярный метод
Копирует подмножество символов строки в массив char
Как видим, методы класса StringBuilder менее развиты, чем методы класса String, но они позволяют более эффективно использовать память за счет работы с изменяемыми строками. Рассмотрим примеры использования данных методов.
С изменяемой строкой можно работать не только как с объектом, но как с массивом символов:
static void Main(){ StringBuilder a = new StringBuilder("2*3=3*2"); Console.WriteLine(a); int k=0; for (int i = 0; i < a.Length; ++i ) if (char.IsDigit(a[i])) k+=int.Parse(a[i].ToString()); Console.WriteLine(k);}
На практике часто комбинируют работу с изменяемыми и неизменяемыми строками. Однако если необходимо изменять строку, то в этом случае используют StringBuilder.
Пример. Дана строка, в которой содержится осмысленное текстовое сообщение. Слова сообщения разделяются пробелами и знаками препинания. Вывести все слова сообщения, которые начинаются и заканчиваются на одну и ту же букву.
static void Main(){ Console.WriteLine("Введите строку: "); StringBuilder a = new StringBuilder(Console.ReadLine()); Console.WriteLine("Исходная строка: "+a); for (int i=0; i<a.Length;) if (char.IsPunctuation(a[i])) a.Remove(i,1); else ++i; string str=a.ToString(); string []s=str.Split(' '); Console.WriteLine("Искомые слова: "); for (int i=0; i<s.Length; ++i) if (s[i][0]==s[i][s.Length-1]) Console.WriteLine(s[i]);}
Задание. Измените программу так, чтобы она корректно работала и для случая, когда в исходной строке встречаются лишние пробелы.
Замечание. При решении задач следует руководствоваться правилом: если в строке должны производится изменения, то лучше пользоваться классом StringBuilder; если необходимо разбивать строки на слова, то классом String. При решении некоторых задач, потребуется использовать оба класса.
Разработать программу, которая для заданной строки s вставляет символ x после каждого вхождения символа y;
using System;using System.Text; namespace ConsoleApplication{ class Class { static void Main() { Console.WriteLine("Введите строку: "); StringBuilder a = new StringBuilder(Console.ReadLine()); Console.WriteLine("Исходная строка: "+a); Console.WriteLine("Введите символ x: "); char x=char.Parse(Console.ReadLine()); Console.WriteLine("Введите символ y: "); char y=char.Parse(Console.ReadLine()); for (int i=0; i<a.Length; ++i) if (a[i]==x){a.Insert(i+1,y); ++i;} Console.WriteLine("Измененная строка: "+a); } }}
Дана строка, в которой содержится осмысленное текстовое сообщение. Слова сообщения разделяются пробелами и знаками препинания. Вывести только те слова сообщения, в которых содержится заданная подстрока.
using System;using System.Text; namespace ConsoleApplication{ class Class { static void Main() { Console.WriteLine("Введите строку: "); StringBuilder a = new StringBuilder(Console.ReadLine()); Console.WriteLine("Исходная строка: "+a); Console.WriteLine("Введите заданную подстроку: "); string x=Console.ReadLine(); for (int i=0; i<a.Length;) if (char.IsPunctuation(a[i]))a.Remove(i,1); else ++i; string str=a.ToString(); str=str.Trim(); string []s=str.Split(' '); Console.WriteLine("Искомые слова: "); for (int i=0; i<s.Length; ++i) if (s[i].IndexOf(x)!=-1) Console.WriteLine(s[i]); } }}
Самостоятельная работа. Работа с командной строкой.