Особенности работы со строковыми переменными в 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, имея в виду следующие цели:

  1. Показать, что создание таких процедур вполне доступно программистам, работающим на ранних версиях VB. Более того, это может пригодиться и тем, кто уже использует VB 6: все новые функции работают только с символьными данными и не имеют вариантов для двоичных байтов (на эту тему мы говорили в первой части статьи).
  2. Проанализировать разные варианты алгоритмов обработки строк, в том числе с использованием байтовых переменных.
  3. Еще раз продемонстрировать, что более простые программные конструкции не всегда оказываются оптимальными с точки зрения скорости обработки данных.

 

В начало

В начало

Ищем животных одного цвета

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

' Исходная строка: список животных 
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.

В качестве комментария к этим программам нужно отметить следующие моменты:

  1. Замена кода (функция ReverseString)
  2. 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. Но тут надо обратить внимание на то, что инверсия строки обеспечивается переносом пары байтов (один символ занимает два байта).

  3. Замена кода (RevInstrByteMy)
  4. 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 работает с любыми данными и, что интересно, — быстрее всех.

  5. Быстродействие моего варианта ReplaceMy тоже не слишком заметно уступает фирменному Replace.

Полный комплект программных приложений к этой статье находится по адресу: http://www.visual.2000.ru/develop/vb/source/

 

КомпьютерПресс 1'2000