Особенности работы со строковыми переменными в VB. Часть 2
(Окончание. Начало см. в КомпьютерПресс № 10’99)
Оператор Like — сравнение на «похожесть»
Новые функции обработки символьных переменных
Сравнение встроенных и «самострочных» функций
Visual Basic включает достаточно большой набор встроенных функций обработки строковых переменных: преобразование, сравнение, поиск и пр. Многие из них обсуждались в первой части статьи и целом ряде наших советов, которые были опубликованы ранее. В VB 6 появилась большая группа дополнительных функций, о которых и пойдет речь ниже. Но сначала вспомним об одном операторе, который появился в VB уже довольно давно (еще в версии 3), но почему-то далеко не все пользователи VB знают о нем.
Оператор Like — сравнение на «похожесть»
Cинтаксис этого оператора выглядит следующим образом:
result = expression Like pattern
где expression — символьное выражение, а pattern — символьный шаблон. Если выражение «соответствует» шаблону, то результат равен истине (True), в противном случае — лжи (False). Если выражение или шаблон равны Null (нулевая строка), то и результат будет равен Null.
Для создания шаблонов можно использовать набор символов обобщения.
Для задания набора символов можно использовать знак дефиса для определения диапазона (но обязательно в порядке возрастания ANSI-кодов). При этом допускается применение нескольких диапазонов. Например, [a-zA-Z0-9] позволит выбрать все алфавитно-цифровые символы для английского языка.
Варианты использования разных типов сравнения приведены в таблице 1.
Оператор Like можно использовать также в SQL-запросах, например, так:
SELECT * FROM Employees WHERE LastName Like "[A-D]"
В этом случае будут выбраны записи о сотрудниках, фамилии которых начинаются с букв A, B, C, D.
Сортировка символов и учет чувствительности регистра (строчные или прописные буквы) выполняется на основе установки оператора Option Compare (по умолчанию он равен 0 — Binary) в данном программном модуле. Это нужно иметь в виду, потому что другие функции сравнения (InStr, StrComp) позволяют указывать режим сравнения (режим поиска: двоичный, текстовый, специальный) непосредственно в качестве параметра функции. Поэтому если вы хотите управлять такими режимами самостоятельно (например, когда в одном модуле желательно иметь и тот, и другой вариант), то можно предложить такое решение.
Сделайте два отдельных BAS-модуля следующего содержания:
Option Compare 0 ' Binary, двоичный Public Function LikeBinary(expression$, pattern$) As Boolean LikeBinary = expression$ Like pattern$ End Function
LikeBinary.bas
Option Compare 1 ' Text, текстовый Public Function LikeText(expression$, pattern$) As Boolean LikeText = expression$ Like pattern$ End Function
LikeText.bas
Далее обращайтесь к функциями LikeBinary или LikeText соответственно. Следует иметь в виду, что правила сравнения для национальных языков определяются текущей кодовой таблицей (для VB до версии 5 включительно) или региональными установками Windows (для VB 6). Подробнее об этом читайте в первой части статьи.
Для реализации «нечувствительного к регистру» режима поиска можно использовать и такой простой вариант:
result = UCase$(expression) Like pattern
Однако нужно отметить, что для сортировки символьных переменных с помощью функции StrConv такой вариант не очень подходит, если вы используете русскую букву «ё». При двоичном сравнении «ё» попадает в конец списка, после «я». При текстовом же сравнении обеспечивается не только нечувствительность к регистру, но и перемещение буквы «ё» на ее законное место — между «е» и «ж».
Для оператора Like это также актуально, если вы используете поиск по шаблону с использованием диапазонов. В частности, выполнение строки
Print «ёж» Like "[а-я]*
приведет к получению результата True для режима Option Compare Text и результата False для Option Compare Binary.
Это тем более существенно, если вы работаете с расширенными символами европейских языков (разные «а» с диакрическими знаками – «крышкой», точкой и прочие знаки над основным символом), Более того, для некоторых европейских языков в режиме текстового поиска определенные двухсимвольные комбинации обрабатываются как одна уникальная буква (например, в испанском языке ch является одной буквой и ставится при сортировке между c и d). И наоборот, один символ преобразуется в два знака: в частности, в немецком символ Я (eszett) эквивалентен ss.
Новые функции обработки символьных переменных
В VB 6 появилось три группы дополнительных функций по обработке символьных переменных. Их краткое описание приведено в таблицах 2, 3, 4, а с более подробными сведениями о них можно ознакомиться в документации о системе.
Конечно, эти новые функции будут полезны при разработке программ, хотя лично я довольно скептически отношусь к идее подобного расширения состава функций. На самом деле все эти новшества могут быть довольно легко реализованы средствами VB, имеющимися даже в версии 3 (а может быть, даже в более ранней — с версиями 1 и 2 я не знаком). Их появление в составе VB может произвести впечатление на новичка, но не на опытного программиста. Проблема-то заключается в том, что для более сложных операций обработки строк все равно потребуется «ручное кодирование» собственных процедур.
В этом плане было бы гораздо полезнее, если бы Microsoft вместо довольно бессмысленной гонки по расширению встроенных функций реализовала простой механизм подключения повторно используемых процедур на уровне объектных OBJ-библиотек (как это было сделано в свое время еще 15 лет назад в MS QuickBasic для DOS). Но эти рассуждения, конечно, из области ностальгических воспоминаний: Microsoft — «большой, ему видней». Корпорация категорически не желает давать VB-программистам возможность использования OBJ-библиотек.
Далее мы попробуем реализовать некоторые альтернативные варианты новых символьных функций VB 6, имея в виду следующие цели:
- Показать, что создание таких процедур вполне доступно программистам, работающим на ранних версиях VB. Более того, это может пригодиться и тем, кто уже использует VB 6: все новые функции работают только с символьными данными и не имеют вариантов для двоичных байтов (на эту тему мы говорили в первой части статьи).
- Проанализировать разные варианты алгоритмов обработки строк, в том числе с использованием байтовых переменных.
- Еще раз продемонстрировать, что более простые программные конструкции не всегда оказываются оптимальными с точки зрения скорости обработки данных.
Ищем животных одного цвета
Попробуем написать программу для решения следующей задачи. Имеется символьная строка с полями данных; нужно сформировать новую строку, собрав в нее из исходной только те поля, которые содержат заданный ключ. Например, эта задача может выглядеть так:
' Исходная строка: список животных Animals$ = "Белая Собака,Черная Лошадь,Красная Кошка,_ черная птица,Черный ризеншнауцер,голубая кошка" Delim1$ = "," ' разделитель в исходной строке Search$ = "черн" ' ключ поиска vbComp% = vbText Compare ' режим поиска -- текстовый Delim2$ = "//" ' разделитель в результирующей строке 'обращение к процедуре, решающей поставленную задачу: Result$ = Test$(Animals$, Delim1$, Search$, vbComp%, Delim2$) Print Result$ ' должно быть напечатано: ' Черная Лошадь//черная птица//Черный ризеншнауцер
С использованием новых функций VB 6 написать нужную процедуру достаточно просто:
Public Function Test1$ _ (Source$, Delim1$, Search$, vbComp%, Delim2$) Dim arr1$(), arr2$() ' преобразуем строку в массив. ' разделитель -- Delim1$ arr1$ = Split( Source$, Delim1$) ' Из массива выделяем поля со словом Search$ ' в режиме сравнения vbComp% arr2$ = Filter(arr1$, Search$, , vbСоmp%) ' объединение массива в одну строку Test1$ = Join(arr2, Delim2$) End Function
При использовании традиционных возможностей Basic, которые имелись еще в версиях 15-летней давности, реализация такого алгоритма будет выглядеть посложнее (см. листинг 1). Однако самое удивительное, что второй вариант работает в полтора раза быстрее, чем первый! Почему это происходит — понятно: сами функции Split, Filter и Join работают достаточно быстро, но постоянное создание динамических массивов требует довольно много времени. Поэтому если для вас действительно важна скорость обработки (например, при работе с большими объемами данных), то, может быть, имеет смысл потратить лишних 10 минут, чтобы написать не 4, а 16 строк программного кода.
Сравнение встроенных и «самострочных» функций
Для иллюстрации идеи реализации различных алгоритмов преобразования символьных строк предлагаю вам посмотреть на несколько вариантов процедур, которые являются аналогами новых встроенных функций StrReverse, InstrRev и Replace (соответственно, листинги 2, 3 и 4). Время их выполнения приведено в таблице 5.
В качестве комментария к этим программам нужно отметить следующие моменты:
- Замена кода (функция ReverseString)
- Замена кода (RevInstrByteMy)
- Быстродействие моего варианта ReplaceMy тоже не слишком заметно уступает фирменному Replace.
For i = Len(Source$) To 1 Step -1 Reverse$ = Reverse$ + Mid$(Source$, i, 1) Next
на код (функция ReverseStringMy)
Reverse$ = Space$(Len(Source$)) For i = Len(Source$) To 1 Step -1 j = j + 1: Mid$(Reverse$, j, 1) = Mid$(Source$, i, 1) Next
приводит к увеличению быстродействия на 25% (использование коррекции переменной оператором Mid вместо слияния двух переменных). Однако быстрее всего работает вариант с использование байтового массива ReverseStringByteMy. Но тут надо обратить внимание на то, что инверсия строки обеспечивается переносом пары байтов (один символ занимает два байта).
bytArray = Source$ iBytes = Len(Source$) * 2 - 2 For i = iBytes To 0 Step -2 If Chr(bytArray(i)) = SubString Then RevInstrByteMy = i / 2 + 1: Exit Function End If Next
на код (RevInstrByteMy2)
bytArray = Source$: bytSub = Asc(SubString) iBytes = Len(Source$) * 2 - 2 For i = iBytes To 0 Step -2 If bytArray(i) = bytSub Then RevInstrByteMy2 = i / 2 + 1: Exit Function End If Next
увеличивает производительность в 3,5 раза. В этом случае скорость повышается за счет перехода к использованию байтового массива. Интересно, что оба эти варианта не являются точными аналогами InstrRev — они работают только в том случае, если разделитель является односимвольной переменной, причем с ASCII-кодом из нижней части таблицы (< 128). А универсальный вариант InstrReverseMy работает с любыми данными и, что интересно, — быстрее всех.
Полный комплект программных приложений к этой статье находится по адресу: http://www.visual.2000.ru/develop/vb/source/
КомпьютерПресс 1'2000