Шаг 97.
Язык программирования C#. Начала
Работа с текстом. Операции с текстовыми объектами (окончание)

    На этом шаге мы закончим рассмотрение основных операций с текстовыми объектами.

    Мы уже знаем, что к текстовым значениям может применяться такая арифметическая операция, как сложение. Если складываются (с помощью оператора + ) два текстовых значения, то создается новый текстовый объект, который содержит текст, получающийся объединением складываемых текстовых значений. Результатом выражения возвращается ссылка на созданный объект.


Объединение текстовых строк называется конкатенацией строк.

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

  String txt = "Четыpe " + 2 + 2;

    Вопрос в том, какой текст в итоге присваивается переменной txt. Ответ такой: переменной txt присваивается текст "Четыре 22". Выражение "Четыре "+2+2 вычисляется слева направо. Сначала вычисляется значение выражения "Четыре "+2. В этом выражении один операнд текстовый, а другой целочисленный. Целочисленный операнд приводится к текстовому типу, складываемые текстовые строки объединяются, и в итоге получается строка "Четыре 2". К этому текстовому значению прибавляется целочисленное значение 2, то есть вычисляется значение выражения "Четыре 2"+2. Второй целочисленный операнд приводится к текстовому типу, и в итоге получаем строку "Четыре 22". Результат будет иным, если при определении значения переменной txt воспользоваться следующей командой:

  String txt = "Четыpe " + (2 + 2);

    В этом случае круглые скобки изменяют порядок выполнения операций: сначала вычисляется значение выражения 2+2 (получаем число 4), а затем полученное значение прибавляется к текстовой строке "Четыре ". В итоге переменной txt присваивается текст "Четыре 4".


Для объединения строк можно использовать статический метод Concat() класса String. Аргументами методу передаются текстовые строки (или массив из текстовых строк). Результатом метод возвращает текстовую строку, получающуюся объединением аргументов метода (или элементов текстового массива). При объединении текстовых строк полезным может быть и метод Join(). Это еще один статический метод класса String. Аргументами методу передается текстовая строка, являющаяся разделителем при объединении текстовых строк, и массив с объединяемыми текстовыми строками. Результатом метода возвращается текстовая строка, которая получается объединением текстовых элементов массива (второй аргумент метода), и при объединении строк используется разделитель, определяемый первым аргументом метода.

    Еще одна операция, имеющая важное прикладное значение сравнение текстовых строк на предмет равенства/неравенства. Выполняется сравнение строк с помощью операторов

Если А и В - объектные переменные класса String, то результатом выражения А==В является значение true, если объекты, на которые ссылаются переменные А и В, содержат одинаковый текст. Если эти объекты содержат разный текст, то значение выражения А==В равно false.

    Значение выражения А!=В равно true в случае, если переменные А и В ссылаются на объекты, содержащие разный текст. Если эти объекты содержат одинаковый текст, то значение выражения А!=В равно false.


Если А и В являются объектными и переменными некоторого класса, то в общем случае при вычислении значения выражения А==В или А!=В сравниваются фактические значения переменных - то есть адреса объектов, на которые ссылаются переменные. Поэтому, например, значение выражения А==В равно true, если переменные А и В ссылаются на один и тот же объект. А если объекты физически разные (пускай при этом и совершенно одинаковые), то результатом выражения А==В будет значение false. Подобная ситуация имеет место и при вычислении выражения вида А!=В. Но это в общем случае.

    А для текстовых объектов (то есть когда А и В являются объектными переменными класса String) при вычислении выражений А==В и А!=В сравниваются не адреса объектов, а сами объекты. То есть для объектов класса String сравнение с помощью операторов == и ! = выполняется по-особому. Отметим, что для сравнения текстовых объектов также может использоваться метод Equals(). Благодаря механизму перегрузки операторов, существующему в С#, для объектов пользовательских классов можно изменить способ сравнения объектов.


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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr97_1
{
    class Program
    {
        // Статический метод для сравнения текстовых строк: 
        static bool StrCmp(String X, String Y) { 
            // Если строки разной длины: 
            if (X.Length != Y.Length) return false;
            // Если строки одинаковой длины: 
            for(int k = 0; k < X.Length; k++) {
                // Если символы в текстовых строках разные: 
                if (X[k] != Y[k]) return false;
            }
            // Результат метода, если строки одинаковой длины 
            // и все символы в текстовых строках совпадают: 
            return true;
        }

        // Главный метод:
        static void Main()
        {
            // Символьный массив: 
            char[] smb = {'Я', 'з', 'ы', 'к', ' ', 'C', '#'};
            // Текстовый объект:
            String A = "Язык C#";
            // Отображение текстовой строки:
            Console.WriteLine("А: \"{0}\"", A);
            // Создание нового текстового объекта:
            String B = new String(smb);
            // Отображение текстовой строки:
            Console.WriteLine("В: \"{0}\"", B);
            // Еще один текстовый объект:
            String C = "ЯЗЫК C#";
            // Отображение текстовой строки:
            Console.WriteLine("С: \"{0}\"", C);
            Console.WriteLine("Сравнение строк");
            // Сравнение текстовых строк:
            Console.WriteLine("А==В: {0}", A==B);
            Console.WriteLine("StrCmp(А, В): {0}", StrCmp(A, B));
            Console.WriteLine("А==С: {0}", A==C);
            Console.WriteLine("StrCmp(А, С): {0}", StrCmp(A, C));
            Console.WriteLine("В!=С: {0}", B!=C);
            Console.WriteLine("StrCmp(А,\"C#\"): {0}", StrCmp(A,"C#"));
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

    При выполнении программы получаем такой результат.


Рис.1. Результат работы приложения

    В этой простой программе с помощью операторов == и ! = сравнивается несколько текстовых строк. Также в программе есть описание статического метола StrCmp(), предназначенного для сравнения текстовых строк. Анализ программы начнем с кода метода StrCmp().


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

    Метод StrCmp() описан как статический в том же классе, что и главный метод программы. Поэтому метод StrCmp() можно вызывать в методе Main() без указания названия класса. У метода StrCmp() два текстовых аргумента (объектные переменные X и Y класса String). Через эти переменные методу передаются текстовые значения для сравнения. Если тексты одинаковые, то метод должен возвращать результатом логическое значение true. Если сравниваемые тексты отличаются, то результатом метода должно быть значение false. При выполнении кода метода сначала сравнивается длина текстовых значений. Понятно, что если два текста имеют разную длину, то совпадать они не могут. Это, так сказать, формальная проверка. Выполняется она с помощью условного оператора (в упрощенной форме), в котором проверяется условие X.Length != Y. Length. Если условие истинно, то инструкцией

  return false;
выполнение кода метода завершается, а результатом возвращается значение false. Если же условие ложно, то начинает выполняться конструкция цикла, в которой посимвольно проверяется содержимое двух текстовых аргументов метода. Здесь мы учли, что раз уж цикл выполняется, то обе текстовые строки имеют одинаковую длину (иначе выполнение метода завершилось бы при выполнении условной конструкции). В теле конструкции цикла для каждого фиксированного значения индекса k с помощью условной конструкции проверяется условие X[k] != Y[k]. Истинность этого условия означает, что две буквы, находящиеся на одинаковых позициях, отличаются. В таком случае командой
  return false;
завершается выполнение кода метода, а результатом метода возвращается значение false. Команда
  return true; 
в самом конце тела метода выполняется только в том случае, если при выполнении цикла оказалось, что все буквы (на одинаковых позициях) в сравниваемых текстовых значениях совпадают. Это означает, что текстовые значения одинаковые и результатом метода возвращается значение true.

    В главном методе создается несколько текстовых объектов, которые затем сравниваются (с помощью операторов == и !=, а также метода StrCmp()). Объект А создается присваиванием литерала "Язык С#". Объект В создается на основе предварительно объявленного и инициализированного символьного массива, но в итоге получается объект с таким же текстовым значением, как и объект А. Объект С получает значением текстовый литерал "ЯЗЫК С#". От значений объектов А и В данный текст отличается лишь регистром букв.


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

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


    Для сравнения текстовых значений используются выражения А==В (равенство текстовых значений в объектах, на которые ссылаются переменные А и В результат равен true), А==С (проверка на предмет совпадения текстовых значений в объектах, на которые ссылаются переменные А и С - результат равен false), В!=С (проверка на предмет неравенства текстовых значений в объектах, на которые ссылаются переменные В и С - результат равен true). Аналогичные проверки выполняются с помощью описанного в программе статического метода StrCmp(). Как несложно заметить, результаты проверок вполне ожидаемые.


Хотя для сравнения текстовых значений обычно используют операторы == и !=, это не единственный способ сравнения текстовых строк. Для сравнения текстовых строк можно использовать метод Equals() (вообще, диапазон применения метода Equals() более широк, чем сравнение текстовых значений - этот метод используется для сравнения объектов разных классов, в том числе и объектов класса String). У метода есть несколько версий. Имеется статическая версия метода, которая вызывается из класса String. Аргументами методу в таком случае передаются сравниваемые текстовые строки. Результатом метод возвращает логическое значение true (строки совпадают) или false (строки не совпадают). Нестатическая версия метода вызывается из текстового объекта, а еще один текстовый объект передается аргументом методу. Сравнивается текстовое значение для объекта, из которого вызывается метод, и текстовое значение для объекта, переданного аргументом методу. Результатом метода является логическое значение. Например, если А и В являются объектными переменными класса String, то результатом выражения A.Equals(В) или String.Equals(А, В) является значение true, если переменные А и В ссылаются на текстовые объекты с одинаковым текстом. В противном случае результатом является значение false. Текстовые значения по умолчанию сравниваются с учетом состояния регистра (то есть большая и маленькая буквы считаются разными символами). Если нужно провести сравнение текстовых значений без учета состояния регистра (то есть при сравнении текстов большие и маленькие буквы интерпретировать как один и тот же символ), дополнительным аргументом методу Equals() следует передать выражение StringComparison.OrdinalIgnoreCase (константа OrdinalIgnoreCase перечисления StringComparison). Так, при выполнении команд вида А.Equals(В, StringComparison.OrdinalIgnoreCase) или String.Equals(А, В, StringComparison.OrdinalIgnoreCase) сравнение текстовых значений выполняется без учета состояния регистра. Кстати, если использовать вместо константы OrdinalIgnoreCase константу Ordinal, то при сравнении текстовых строк используется режим по умолчанию, при котором регистр букв имеет значение.

    Иногда для сравнения текстовых значений без учета состояния регистра можно текстовые значения предварительно перевести в верхний или нижний регистр. Для перевода символов текста в верхний регистр используется метод ToUpper(), а для перевода символов текста в нижний регистр используют метод ToLower() (методы обсуждаются в следующих шагах). Например, чтобы сравнить значения текстовых переменных А и В на предмет равенства без учета состояния регистра, можно использовать выражения A.ToUpper()==В.ToUpper() или A.ToLower()==B.ToLower(). При этом значения переменных А и В не меняются.


    На следующем шаге мы рассмотрим методы для работы с текстом.




Предыдущий шаг Содержание Следующий шаг