Исследование MS Visual С#, страница 19

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

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

Перед тем, как начать писать код, следует иметь в виду, что строки в C# являются неизменяемыми (immutable). Это означает, что компилятор не позволяет работать со строкой, как с массивом символов в режиме записи, однако в режиме чтения (readonly) такое возможно. Обойти это препятствие позволит специальный класс StringBuilder, определеный в пространстве имен System.Text. Он поддерживает изменяемые (mutable) строки текста и мы используем его для того, чтобы посимвольно копировать элементы старой строки в новую. Алгоритм может быть таким. В цикле пробега по всем символам исходной строки мы либо копируем символы в новую строку sNew, либо игнорируем их. Размер новой строки может быть либо равен, либо меньше размера старой. Перед тем, как возвратить результат, надо привести объект класса StringBuilder к типу string.

private string Trim (string sOld)

{

StringBuilder sNew = new StringBuilder ();  // Создаем новую пустую строку

  //==== Предполагаем, что сначала идут пробелы

  //==== Ваш код

return sNew.ToString();           // Результат вновь приведем к типу string

}

После создания функции Trim вставьте ее вызовы внутрь метода Add. Место вставки определите самостоятельно.

tName.Text = Trim (tName.Text);

tProf.Text = Trim (tProf.Text);

tPhone.Text = Trim (tPhone.Text);

tAddr.Text = Trim (tAddr.Text);

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

private void bDel_Click(object sender, System.EventArgs e) { Delete(); }

Введите вспомогательную функцию Delete, которая понадобится еще раз (отклик на клавишу Delete).

private void Delete()

{

int id = list.SelectedIndex;    // Находим в списке выделенный элемент

if (id == ListBox.NoMatches) // Если такого нет, уходим

  return;

sUndo = list.Items[id].ToString(); // Запоминаем удаленный элемент

list.Items.RemoveAt(id);// Удаляем его из списка

bUndo.Enabled = true; // Кнопка "Отмена" становится доступной

int size = list.Items.Count;

 // Скорректируйте переменные sTotal, bModified . . .

if (size == 0)

{ // Если список опустошен, очистите поля ввода

      return;

}

// Скорректируйте индекс текушего элемента if (id  и т.д.

}

Возвращаясь к ошибке в алгоритме вставки в список новых элементов, приведем набор входных данных, на которых она обнаруживается. Это последовательность имен: "Victor" и "Vic". Попытайтесь вставить одного, а затем другого. Если вы не нашли ошибку, то сделайте это сейчас и исправьте ее. Создайте обработчик нажатия кнопки Найти.

private void bFind_Click(object sender, System.EventArgs e)

{

tName.Text = Trim(tName.Text); // Нормализуем имя человека

int id = list.FindString (tName.Text); // Ищем элемент по имени

if (id == ListBox.NoMatches)    // Если не находим, высвечиваем текст

  sNotFound.Show();

else

  list.SelectedIndex = id;    // Иначе, выделяем найденный элемент

}

Реакции на уведомляющие сообщения

Для того, чтобы ввести в класс Form1 реакцию на выбор какой-либо строки в окне списка:

1.  В режиме Design поставьте фокус в окно списка (list) и в окне Properties нажмите кнопку Events.

2.  Найдите в списке событий строку с именем SelectedIndexChanged и дважды щелкните над ней.

В классе формы появится функция реакции на уведомление от ListBox. Вставьте в нее код.

private void list_SelectedIndexChanged (object sender, EventArgs e)

{

sNotFound.Hide(); // Убираем сообщение

int id = list.SelectedIndex; // Определяем индекс выделенной строки

if (id != ListBox.NoMatches) // Защита от побочных эффектов

{

  string s = list.Items[id].ToString(); // Выуживаем текстовую строку

      //== Разбиваем ее на 6 подстрок При этом используется массив разделителей

  string[] ss = s.Split (new Char[] {';', ':'}, 6);

  tName.Text = Trim (ss[0]);           // Выбираем только нужные подстроки

  tProf.Text = Trim (ss[1]);

  tPhone.Text = Trim (ss[3]);

  tAddr.Text = Trim (ss[5]);

}

}

Здесь использован неподражаемый метод Split, который присутствует в классе String. Он возвращает массив строк текста, а точнее, массив новых объектов типа string. Список разделителей, поданный вторым аргументом, позволяет выделить все необходимые нам лексемы из строки, хранящейся в ListBox’е.