Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: DocOne8, DocTwo8.
В любом языке программирования допустимы выражения. Нужно уметь выражаться корректно. Выражения строятся из переменных, констант, встроенных функций с использованием знаков операций и скобок. Запись выражения задает правило (алгоритм) вычисления его значения и его типа. Естественно, что тип и значения всех его переменных должны быть определены до момента вычисления выражения. Языки программирования различаются между собой тем, до какой степени они допускают автоматическое X+Y вещественный тип для переменной X и целочисленный тип для переменной Y, проводя автоматическое преобразование к вещественному типу и выполняя, затем уже, сложение вещественных чисел. Язык VBA в этом отношении занимает золотую середину. Он не столь строг, как классические языки, предложенные Никласом Виртом - Паскаль, Модула, Оберон, но и не допускает особых вольностей. Среди его встроенных функций есть большое число функций, предназначенных для явного преобразования типов, что позволяет программировать в лучших традициях надежных языков программирования, не доверяя
Тема построения выражений обширна, достаточно сказать, что число встроенных функций измеряется не десятками, а сотнями. Подробное их описание не входит в нашу задачу, для этого есть справочная система, но общий обзор и некоторые детали мы постараемся привести.
Приведем основные операции, которые можно выполнять над данными в языке VBA, классифицируя их по типу и приоритету:
| Приоритет | Арифметические | Сравнения | Логические | Описание некоторых операций |
|---|---|---|---|---|
| 1 | Возведение в степень - (^) | Равенство - (=) | Отрицание - (Not) | При возведении в степень основание и показатель могут быть арифметическими выражениями любого типа. Результат имеет тип Double. |
| 2 | Унарный минус - (-) | Неравенство - (<>) | Конъюнкция - (And) | |
| 3 | Умножение, Деление - (*, /) | Меньше - (<) | Дизъюнкция - (Or) | |
| 4 | Деление нацело - (\) | Больше - (>) | Исключительное Или - (Xor) | Деление нацело определено над целочисленными данными (применимо и к вещественным данным) и дает результат целого типа. Исключительное Или требует, чтобы один из операндов имел значение, отличное от True |
| 5 | Остаток от деления нацело - (mod) | Меньше или равно - (<=) | Эквивалентность - (Eqv) | Операция mod определена над данными целого типа и возвращает результат целого типа - остаток от деления нацело. |
| 6 | Сложение, вычитание - (+,-) | Больше или равно - (>=) | Импликация - ( |
Среди логических операций определена операция следования (импликация), ложная в единственном случае, когда посылка истинна, а заключение ложно. |
| 7 | Конкатенация строк - () | Подобия - ( Like ), Равенство ссылок - ( Is ) |
Операция Like проверяет соответствие строки образцу. Операция Is, определенная над объектами, не проверяет равенство самих объектов, она проверяет совпадение ссылок. Ссылки должны задавать один и тот же объект. |
Если выражение содержит операции разных категорий, то первыми выполняются арифметические операции, затем, операции сравнения и последними - логические.
Все операции сравнения имеют один и тот же приоритет. Арифметические и логические операции выполняются в соответствии с указанным приоритетом.
Одна и та же операция, записанная несколько раз подряд, или операции одного приоритета (умножение и деление, сложение и вычитание) выполняются слева направо, - из двух операций первой выполняется та, которая стоит левее в записи выражения.
Скобки позволяют изменить указанный порядок вычислений выражения, поскольку выражения в скобках имеют наивысший приоритет и вычисляются первыми. Внутри скобок действует обычный порядок вычисления.
Операция конкатенации не является арифметической, но при ее появлении в выражении она выполняется после всех арифметических операций, но до вычисления операций сравнения.
Арифметика в VBA представлена достаточно полно, Напомним, что арифметический тип подразделяется на подтипы:
Byte, Integer, Long - для представления целочисленных данных.Single, Double - для представления вещественных данных.Decimal - для представления чисел в форме с фиксированной точкой, что важно, в частности, для финансовых вычислений.Currency - специальный тип для представления денежных данных.Variant - обобщенный тип, позволяющий хранить и обрабатывать данные разного типа.Возможные арифметические операции мы уже упомянули.
Пример работы с числовыми данными:
Public Sub WorkWithArithmetic() Dim X As Integer, Y As Integer Dim U As Single, V As Single Dim Z As Double U = 15.8: V = -6.5 Z = U / V: X = CInt(U / V): Y = U \ V Debug.Print X, Y, Z, U, V, X \ Y, X Mod Y, U Mod V End Sub
Вот результаты печати в окне отладки:
-2 -2 -2,43076926011306 15,8 -6,5 1 0 4
Заметим, что хотя целочисленные операции возможны над вещественными данными, применять их не следует, поскольку это один из тех случаев, когда выполняются внутренние преобразования, точная интерпретация которых сложна, что и может, в конечном итоге, служить источником программистских ошибок.
Рассмотрим основные встроенные математические функции.
Набор математических функций VBA достаточно стандартный. Перечислим их с краткими пояснениями:
Abs(число) - абсолютное значение числа.Atn(число) - арктангенс (в радианах) аргумента, задающего тангенс угла.Cos(число) - косинус угла. Аргумент число задает угол в радианах.Exp(число) - экспонента, т. е. результат возведение числа e (основание натуральных логарифмов) в указанную степень.Log(число) - натуральный логарифм числа.Rnd[(число)] - результат представляет равномерно распределенное случайное число в интервале [0 - 1]. Если аргумент число не задан или больше нуля, то порождается очередное случайное число, если он равен 0, то результатом будет предыдущее случайное число, а если число меньше нуля, то всякий порождается одно и то же число, определяемое аргументом. Перед тем, как получить последовательность случайных чисел необходимо вызвать функцию Randomize для инициализации последовательности. Заметим, для формирования значения случайных чисел используется таймер. Чтобы получить целочисленную последовательность равномерно распределенных случайных чисел в интервале [Min - Max], следует использовать следующее преобразованиеInt((Max - Min +1)*Rnd)+ Min
В примерах предыдущей лекции мы неоднократно использовали эту функцию для инициализации массивов с числовыми данными.
Sgn(число) - знак числа (Если число больше нуля - 1, равно нулю - 0, меньше нуля - -1 ).Sin(число) - синус угла. Аргумент число задает угол в радианах.Sqr(число) - квадратный корень.Tan(число) - тангенс угла. Аргумент число задает угол в радианах.Во всех этих описаниях под аргументом функции число понимается числовое выражение.
В справочной системе или в любом математическом справочнике Вы сможете найти указания, как на основе этого базиса, можно вычислить большое число других математических функций. Например, вычисление гиперболического синуса производится по формуле:
HSin(X) = (Exp(X) - Exp(-X))/2
а вычисление арккотангенса:
Arccotan(X) = Atn(X) + 2*Atn(1)
Работать с текстами программисту, как правило, приходится значительно чаще, чем работать с числами. Поэтому следует хорошо представлять основные базисные операции над строками, которые в большинстве случаев реализуются с помощью встроенных функций. Заметим, что в VBA Office 2000 добавлены новые операции над строками, расширяющие возможности эффективной работы по преобразованию текста.
Обычные операции сравнения применимы и к строковым данным. Мы уже говорили ранее о том, что интерпретация этих операций зависит от установки опции Option Compare.
Text, то сравнение на "больше - меньше" представляет лексикографическое сравнение, когда строки сравниваются по их расположению в словаре. Заметьте, это сравнение не чувствительно к регистру, так что большие и малые буквы не различаются. Программисты, конечно, понимают, что сравнение строк означает сравнение кодов их символов, так что Binary, то сравнение идет побитно. В этом случае сравнение естественно, чувствительно к регистру.Если нужно локально переопределить вид сравнения, заданный опцией для всего модуля, то можно использовать встроенную функцию StrComp, которая возвращает результат сравнения строк. Ее синтаксис:
StrComp(string1, string2[, compare])
Аргументы string1 и string2 - сравниваемые строки. Необязательный аргумент compare указывает способ сравнения строк: значение по умолчанию 0 используется, чтобы выполнить двоичное сравнение, 1 задает посимвольное сравнение без учета регистра.
Если string1 меньше чем string2, то результат равен -1, если строки равны, то - 0, если вторая меньше, то - 1, если хоть одна из строк имеет значение Null, то результат также равен Null.
Мощным и весьма полезным средством при работе с текстами является операция Like, задающая сравнение с образцом. Необходимость нахождения в наборе всех строк, удовлетворяющих некоторому шаблону (образцу), возникает в самых разнообразных задачах. VBA позволяет решать ее в одну операцию. Приведем таблицу специальных символов, допустимых при задании образца.
| Символы | Интерпретация | Примеры |
|---|---|---|
* |
Любой текст - произвольное число символов | Шаблону Agent* соответствуют все тексты, начинающиеся со слова Agent. Строки Agent007 и Agent Майор Пронин удовлетворяют шаблону. |
| ? | Один любой символ | Шаблону К?к удовлетворяют, в частности строки Кок и Кук. |
# |
Любая цифра от 0 до 9 | Шаблону Agent### соотвествуют 1000 различных строк, среди которых и Agent007, но, конечно же, не Agent Майор Пронин. |
[множество_символов] |
Любой символ, принадлежащий множеству | Задать множество можно с помощью перечисления и интервалов. Шаблону К[аоу]к удовлетворяют слова "Как", "Кок", "Кук". Чувствительность к регистру зависит от установки опции Option Compare. |
[!множество_символов] |
Любой не принадлежащий множеству символ | Шаблону [!а-я] удовлетворяет символ, не являющийся буквой русского алфавита. |
Приведем пример работы с операцией Like:
Public Sub LikeOperation() Const pat1 = "[A-Z]" Const pat2 = "[a-z]" Const pat3 = "[!a-z]" Const pat4 = "[3-5]" Dim res As Byte Dim Sym As String res = "Кук" Like "К[аоу]к" Debug.Print res res = "f" Like pat1 Debug.Print res res = "f" Like pat2 Debug.Print res res = "f" Like pat3 Debug.Print res res = "5" Like pat4 Debug.Print res Sym = "3" res = Sym Like pat1 pat4 Debug.Print res res = Sym Like pat1 Or Sym Like pat4 Debug.Print res End Sub
Вот результаты отладочной печати:
255 0 255 0 255 0 255
Обратите внимание на последние два результата, демонстрирующие некорректный и корректный способы работы с объединением множеств проверяемых символов.
В классической математике давным - давно определен набор основных операций над числовыми и булевыми данными. Строковой арифметикой серьезно стали заниматься с появлением компьютеров. Жесткий стандарт на эти операции еще не сложился. Как правило, во всех языках есть только одна операция, называемая конкатенацией строк, обозначаемая, обычно, символом "". Все остальные основные операции реализуются с помощью встроенных функций, имена которых и их аргументы могут варьироваться от языка к языку. Более того, варьируется и сам набор этих операций. Минимально, помимо конкатенации необходимы еще две операции, первая из которых позволяет обнаружить индекс вхождения одной строки в другую, вторая - выделить из строки ее подстроку. VBA имеет достаточно мощный набор операций, который в Office 2000 существенно был расширен принципиально новыми возможностями. Рассмотрим вначале те операции, которые и ранее существовали в языке, а уж потом, чуть более подробно поговорим о новых возможностях.
Функция Len(string) возвращает длину строки, заданной аргументом String. Заметьте, возвращается число символов строки, а не число байтов. Ранее мы уже говорили, что в Office 2000 для внутреннего представления строк используется Unicode кодировка, в которой каждый символ занимает два байта.
Функция InStr определяет позицию (индекс) первого вхождения одной строки внутри другой строки. Синтаксис
InStr([start, ]string1, string2[, compare])
Необязательный аргумент start задает позицию, с которой начинается поиск (по умолчанию - с первого символа строки). Аргумент string1 задает строку, в которой выполняется поиск, а string2 - подстроку, вхождение которой ищется. Необязательный аргумент compare имеет тот же смысл, что и для функции StrComp. Возвращаемые значения определяются следующей таблицей.
| Условия | Значение функции InStr |
|---|---|
string1 - пустая строка |
0 |
string1 или string2 равны Null |
Пустое значение - Null |
string2 - пустая строка |
start |
Вхождение string2 не найдено в string1 |
0 |
Вхождение string2 найдено в string1 |
Позиция обнаруженной подстроки |
start >Len(string2) |
0 |
Например, два вызова этой функции в окне отладки вернут следующие результаты:
? InStr(4, "XXпXXпXXПXXП", "П", 1) 6 ? InStr(4, "XXпXXпXXПXXП", "П", 0) 9
Функция Left( выделяет в строке string указанное число length символов слева, позволяя выделить префикс строки.
Функция Right( выполняет аналогичную операцию, выделяя символы справа, что позволяет получить суффикс (окончание) строки.
Более Mid(string, start[, length]) позволяет выделить из строки string подстроку длины length, начиная с позиции start.
Вот пример вызова этих функций непосредственно из окна отладки:
? VBA.Left("рококо",3)
рок
? VBA.Right("рококо",3)
око
? VBA.Mid("рококо",3,3)
кок
Функции LTrim(string), RTrim(string), Trim(string) возвращают копию строки, из которой удалены пробелы, находившиеся в начале строки ( LTrim ), в конце строки ( RTrim ) или в начале и конце строки ( Trim ).
Функция String создает строку: содержащую заданное число повторяющихся символов.
Синтаксис:
String(number, character)
Аргумент number задает длину строки, а character - код символа или строковое выражение, первый символ которого используется при создании результирующей строки.
Функции LCase(string) и UCase(string) возвращают копию строки, символы которой приведены к нижнему ( Low ) или верхнему регистру ( Upper ).
Функции, возвращающие строки, существуют в двух вариантах, отличающихся именами. Функции, имена которых мы приводили, возвращают результат типа Variant. Во втором варианте имена функций оканчиваются знаком $, например, Mid$, UCase$. В этом случае результат возвращается типа String. Такие функции выполняются быстрее, но не могут корректно работать со строками, имеющими значение Null.
Приведем в качестве примера полезную функцию, которая находит путь к активному документу Word, и производит разбор всех компонент этого пути:
Public Function AppPath(Disk As String, Dir As String, FileName As String) As String 'Эта Функция возвращает в качестве результата полный путь активного документа 'Ее параметры содержат компоненты этого пути - имя диска, каталог на диске и имя файла Dim MyDoc As Document Dim Path As String Dim Start As Byte, Finish As Byte 'Определяем полный путь к файлу, задающему активный документ Word Set MyDoc = ActiveDocument Path = MyDoc.FullName 'Выделяем имя диска - первый символ полного пути Disk = VBA.Left(Path, 1) 'Выделяем каталог, в котором хранится документ Start = VBA.InStr(1, Path, "\") Finish = VBA.InStrRev(Path, "\") Dir = VBA.Mid(Path, Start + 1, Finish - Start) 'Выделяем имя файла FileName = VBA.Mid(Path, Finish + 1) 'Возвращается результат - полный путь к каталогу AppPath = VBA.Left(Path, Finish) End Function Public Sub MyPath() Dim Path As String Dim Dir As String Dim Disk As String Dim FileName As String Path = AppPath(Disk, Dir, FileName) Debug.Print Disk, Dir, FileName, Path End Sub
Вот результаты отладочной печати после запуска процедуры MyPath:
E O2000\VBA2000\Ch8\ Ch8.doc E:\O2000\VBA2000\Ch8\
Обратите внимание, в функции AppPath мы использовали новую функцию InStrRev, что облегчило решение нашей задачи. К описанию вновь введенных функций мы и переходим.
В VBA 2000, как мы уже говорили, добавлены полезные функции для работы со строками.
Функция InStrRev симметрично дополняет функцию InStr, аналогично тому, как функция Right дополняет функцию Left. Эта функция ищет вхождение подстроки в строку, но начинает свою работу с правого конца строки. Ее использование может существенно ускорить работу, если заранее известно, что искомая подстрока находится где- то в конце строки - источника. Если вхождение искомой подстроки единственно, то обе функции дают один и тот же результат. При множественном вхождении функция InStr возвращает первое вхождение, в то время как InStrRev - последнее. Ее синтаксис:
InstrRev(stringcheck, stringmatch[, start[, compare]])
Ее параметры имеют тот же смысл, что и у функции InStr, но, заметьте, порядок их задания изменен. Необязательный параметр Start теперь задается третьим по счету. Когда он опущен, то по умолчанию, его значение равно "-1", и поиск начинается с последнего символа. Если вернуться к нашему последнему примеру, то для решения нашей содержательной задачи требовалось определить первое и последнее вхождение символа "\" в строке, задающей путь к файлу. Поэтому обе функции были весьма кстати. Нужно отметить, что это весьма типичная ситуация при разборах текста.
Это удивительно, что в наборе встроенных функций не было до сих пор функции Replace. Замена одной подстроки на другую - это одна из основных операций при работе со строками. Поэтому в арсенале практически каждого из программистов была своя версия функции Replace. Теперь можно пользоваться стандартной реализацией. Ее несомненным достоинством является то, что она позволяет заменить не только первое вхождение искомой подстроки, но и все такие вхождения, не требуя организации цикла. С другой стороны, можно ограничиться только заменой первого или нескольких первых вхождений. Рассмотрим синтаксис появившейся функции:
Replace(expression, find, replace[, start[, count[, compare]]])
Первый аргумент expression задает строковое выражение, результат которого определяет строку - источник, в которой осуществляется замена. Аргументы find и Replace задают заменяемую подстроку и ее новое значение. Аргумент Count определяет число замен. Обычно он равен 1, когда речь идет о замене первого вхождения, или опускается, - в этом случае его значение по умолчанию равно " -1 ", означающее замену всех вхождений. Аргумент compare имеет обычный смысл.
В качестве примера приведем функцию Rep, которую мы постоянно используем в нашей работе в процессе работы над этой книгой. Все примеры в нашей книге проверены в реальной работе, они копируются из модулей VBA. При копировании программного текста в документ Word появляются последовательности пробелов, отражающие структурную запись процедур и функций. Но появление в документах Word двух пробелов подряд считается смертельным грехом. Заменять пробелы табуляцией можно и руками, но, конечно же, лучше написать макрос, решающий эту и другие задачи по приведению текста в форму, требуемую редакцией. Вот текст функции, используемой для этих целей:
Public Sub Rep() 'Эта процедура преобразует выделенный программный текст 'Заменяя пробелы табуляцией и конец абзаца мягким концом строки Dim TxtRange As String TxtRange = Selection.Range.Text 'Замена пробелов: 4-х, 3-х и 2-х символом табуляции TxtRange = Replace(TxtRange, " ", vbTab) TxtRange = Replace(TxtRange, " ", vbTab) TxtRange = Replace(TxtRange, " ", vbTab) 'Замена концов абзаца Selection.Range.Text = Replace(TxtRange, VBA.Chr(13), VBA.Chr(11)) End Sub
Несмотря на всю полезность функции Replace нам пришлось написать еще одну собственную модификацию этой функции. Дело в том, что у Replace есть одна особенность, на которую следует обратить внимание, - возвращаемый ею результат, начинается не с первой позиции, а с позиции, заданной аргументом Start. Так что помимо своей основной роли она еще обрубает голову строки, если только Start, отличается от 1. Поскольку замену зачастую нужно производить не с самого начала, а получать хочется полную строку, то мы написали свой вариант этой функции. Приведем его текст:
Public Function MyReplace(ByVal Expr As String, ByVal find As String, _ ByVal rep As String, Optional ByVal start As Long = 1, Optional ByVal count As Long = -1, _ Optional ByVal compare As VbCompareMethod = vbBinaryCompare) As String 'Вызов стандартной функции Replace If start = 1 Then MyReplace = replace(Expr, find, rep, start, count, compare) Else MyReplace = VBA.Left(Expr, start - 1) replace(Expr, find, rep, start, count, compare) End If End Function
Вот результаты нескольких вызовов Replace и MyReplace в окне отладки:
? Replace("A+B *(D*B +B)","B","C",4,1)
*(D*C +B)
? MyReplace("A+B *(D*B +B)","B","C",4,1)
A+B *(D*C +B)
? MyReplace("A+B *(D*B +B)","B","C",4)
A+B *(D*C +C)
Удаление вхождений подстроки в строку также является одной из основных операций над строками. Хотя специальной встроенной функции DelStr не появилось, но написать ее реализацию, имея Replace, совсем просто. Удалить подстроку эквивалентно замене ее пустой подстрокой. Вот текст, написанной нами функции DelStr:
Public Function DelStr(ByVal Expr As String, ByVal find As String, _ Optional ByVal start As Long = 1, Optional ByVal count As Long = -1, _ Optional ByVal compare As VbCompareMethod = vbBinaryCompare) 'Вызов функции MyReplace с пустой строкой для замены DelStr = MyReplace(Expr, find, "", start, count, compare) End Function
Приведем результаты ее вызовов в окне отладки:
? DelStr("T* + U** - V*","*")
T + U - V
? DelStr("T* + U** - V*","*",5)
T* + U - V
? DelStr("T* + U** - V*","*",5,1)
T* + U* - V*
Заметьте, при определении DelStr мы предпочли пользоваться собственной модификацией MyReplace, поскольку, как нам кажется, она в большей степени отвечает сути дела.
Важную группу новых функций составляют функции, предназначенные для разбора текста. Их появление позволяет эффективно решать целый класс часто встречающихся задач при работе с текстами. Суть дела такова: текст, заданный строкой, зачастую, представляет совокупность структурированных элементов - абзацев, предложений, слов, скобочных выражений и так далее. При работе с таким текстом необходимо разделить его на элементы, пользуясь тем, что есть специальные разделители элементов, - это могут быть пробелы, скобки, знаки препинания. Практически подобная задача возникает постоянно при работе со структурированными текстами. Новые функции существенно облегчают решение подобных задач. Функция Split позволяет разделить строку на элементы и создать массив из этих элементов. Функция Filter позволяет выделить нужные элементы в этом массиве, а функция Join решает обратную задачу, преобразуя массив в строку. Рассмотрим эти функции подробнее.
Предполагается, что исходная строка состоит из элементов (подстрок), разделенных специальными символами - разделителями. Функция Split возвращает одномерный массив из элементов строки. Ее синтаксис:
Split(expression[, delimiter[, limit[, compare]]])
Ее параметры:
expression - строковое выражение, результат которого задает строку - источник, состоящую из элементов и разделителей.delimeter - строка, задающая последовательность символов, используемых в качестве разделителя. Если этот параметр опущен, то по умолчанию предполагается, что в роли разделителя выступает пробел.limit - необязательный параметр, позволяющий ограничить число возвращаемых элементов. По умолчанию его значение равно -1, означающее выделение всех элементов.compare - необязательный параметр, имеющий стандартный смысл во всех операциях над строками. По умолчанию предполагается двоичное побитовое сравнение.Приведем пример, в котором сложноподчиненное предложение разделяется на простые предложения:
Public Sub SplitString()
'В этой процедуре сложное предложение разделяется на простые
'Объявляем динамический массив
Dim Simple() As String, i As Byte
'Размерность массива Simple устанавливается автоматически
'в соответствии с размерностью массива, возвращаемого функцией Split
Simple = Split("А это пшеница, которая в темном чулане хранится в доме, " _
"который построил Джек", ", ")
For i = LBound(Simple) To UBound(Simple)
Debug.Print Simple(i)
Next i
End Sub
В результате работы этой процедуры создается массив из трех элементов:
А это пшеница которая в темном чулане хранится в доме который построил Джек
Обратите внимание, для использования результатов работы функции Split создается Split, он заполняется элементами и у него появляется нижняя и верхняя граница. Далее с этим массивом можно работать обычным образом.
У функции Split, на наш взгляд, есть один недостаток. Хотя в роли разделителя может выступать последовательность символов, в нашем примере это пара символов "запятая, пробел", но нельзя использовать разные разделители элементов. В задачах подобного рода типичной является ситуация, когда элементы отделяются разными разделителями, возможно, окруженными пробелами. Позже мы покажем, как можно справиться с этой задачей.
Функция Join решает обратную задачу, - она восстанавливает строку по ее элементам, хранящимся в массиве, добавляя разделители в момент их объединения. Ее синтаксис:
Join(sourcearray[, delimiter])
Параметры ее понятны без особых пояснений. Заметим, что если необязательный аргумент опущен, то элементы разделяются пробелами. В качестве примера, приведем обратную сборку сложного предложения:
Public Sub SplitAndJoin()
'В этой процедуре сложное предложение разделяется на простые
'А затем после обработки строка восстанавливается
'Объявляем динамический массив
Dim Simple() As String, i As Byte
Dim Simple1() As String, Res As String
'Размерность массива Simple устанавливается автоматически
'в соответствии с размерностью массива, возвращаемого функцией Split
Simple = Split("А это пшеница, которая в темном чулане хранится в доме, " _
"который построил Джек", ", ")
'Создаем новый массив
ReDim Simple1(1 To UBound(Simple) + 2)
Simple1(1) = "А это веселая птица - синица"
Simple1(2) = "которая часто ворует пшеницу"
For i = 3 To UBound(Simple1)
Simple1(i) = Simple(i - 2)
Next i
'Создаем строку из массива Simple1
Res = Join(Simple1, ", ")
Debug.Print Res
End Sub
Вот результат отладочной печати:
А это веселая птица - синица, которая часто ворует пшеницу, которая в темном чулане хранится в доме, который построил Джек
Функция Filter является одним из вариантов поиска по образцу среди элементов массива. Здесь не требуется точного совпадения элемента и образца, достаточно, чтобы строка - образец содержалась в строке, заданной элементом массива. Поскольку совпадений может быть достаточно много, то результатом является массив отфильтрованных элементов. Некоторые детали будут пояснены при описании аргументов функции Filter. Вот ее синтаксис:
Filter(sourcearray, match[, include[, compare]])
Ее параметры имеют следующий смысл:
sourcearray - одномерный массив, элементы которого являются строками. Он может быть получен, например, как результат расщепления строки в массив.match - образец поиска. Строка, вхождение которой ищется в каждом элементе исходного массива.include - необязательный аргумент булевого типа. По умолчанию имеет значение True, означающее, что элементы, удовлетворяющие образцу, входят в результирующий массив. Если задано значение False, то результирующий массив составляется из элементов, не удовлетворяющих образцу.compare - имеет обычный смысл.В качестве результата возвращается массив отфильтрованных элементов.
Наш пример будет представлять расширенный вариант уже приводившейся процедуры SplitAndJoin:
Public Sub SplitAndJoin()
'В этой процедуре сложное предложение разделяется на простые
'А затем после обработки строка восстанавливается
'Здесь же демонстрируется фильтрация элементов массива
'Объявляем динамический массив
Dim Simple() As String, i As Byte
Dim Simple1() As String, Res As String
Dim Simple2() As String
'Размерность массива Simple устанавливается автоматически
'в соответствии с размерностью массива, возвращаемого функцией Split
Simple = Split("А это пшеница, которая в темном чулане хранится в доме, " _
"который построил Джек", ", ")
'Создаем новый массив
ReDim Simple1(1 To UBound(Simple) + 2)
Simple1(1) = "А это веселая птица - синица"
Simple1(2) = "которая часто ворует пшеницу"
For i = 3 To UBound(Simple1)
Simple1(i) = Simple(i - 2)
Next i
'Создаем строку из массива Simple1
Res = Join(Simple1, ", ")
Debug.Print Res
'Фильтрация элементов массива
Simple2 = Filter(Simple1, "котор")
Res = Join(Simple2, ", ")
Debug.Print Res
Simple2 = Filter(Simple1, "котор", False)
Res = Join(Simple2, ", ")
Debug.Print Res
End Sub
Вот результаты отладочной печати:
А это веселая птица - синица, которая часто ворует пшеницу, которая в темном чулане хранится в доме, который построил Джек которая часто ворует пшеницу, которая в темном чулане хранится в доме, который построил Джек А это веселая птица - синица
Какие бы новые возможности не появлялись в системе, программист всегда будет создавать свой собственный инструментарий, расширяющий стандартные возможности. В предыдущих разделах мы уже приводили разработанные нами функции MyReplace, DelStr, являющиеся частью такого инструментария. Сейчас мы хотим привести еще несколько функций из нашего инструментального набора. Их приведение полезно и по той причине, что эти функции используют новые средства разбора строк. Мы приведем вариации стандартных функций Replace, Filter и Split.
Несмотря на то, что уже один вариант функции Replace приведен, мы разработали еще два варианта. Оба они отражают часто возникающую необходимость производить замену, основанную не на точном совпадении, а на совпадении с шаблоном, как это делается в операции Like.Наши новые функции позволяют разные подстроки заменять одним и тем же новым содержимым. Нам не удалось написать универсальную и эффективную реализацию такой функции, поэтому мы ограничились двумя частными, но важными для практики случаями.
Важным частным случаем, когда легко реализовать замену, основанную на шаблоне, представляют строки, допускающие разбор их на элементы. В этом случае алгоритм замены понятен, - строка разбирается на элементы, используя новые возможности, предоставляемые функцией Split ; затем к каждому элементу применяется операция Like для сравнения с шаблоном; на последнем шаге применяется Join для восстановления строки. Приведем текст нашей функции:
Public Function WildReplace(ByVal expr As String, ByVal find As String, _ ByVal Rep As String, Optional ByVal delimiter As String = " ") As String 'Эта функция применима в том частном случае, когда строка - источник, заданная аргументом Expr 'представляет совокупность элементов, отделяемых разделителями. 'Типичный пример - строка представляет совокупность слов, разделенных пробелами. 'Как и обычная функция Replace эта функция производит замену вхождений элемента (подстроки) новым значением 'Отличие от стандартной функции состоит в том, что заменяемый элемент (подстрока) find 'задается шаблоном. В результате разные элементы могут быть заменены на новое значение. 'Для этого частного случая WildReplace существенно расширяет стандартные возможности Replace 'Алгоритм основан на том факте, что строка допускает разбор ее на элементы, 'а к элементам применима операция Like - сравнения с шаблоном. Dim Words() As String, i As Long, Res As String 'Разбор строки на элементы Words = Split(expr, delimiter) 'Сравнение элементов с шаблоном и замена в случае совпадения For i = LBound(Words) To UBound(Words) If Words(i) Like find Then Words(i) = Rep Next i 'Сборка строки Res = Join(Words, delimiter) WildReplace = Res End Function
Параметры этой функции имеют тот же смысл, что и для стандартной функции Replace. Существенное отличие состоит в том, что специальные символы шаблона допустимы в строке поиска find. Приведем теперь WildReplace:
Public Sub testWildReplace() Dim Inf As String, SecretInf As String Inf = "Agent001, Agent007, Агент Майор Пронин, Agent008" SecretInf = WildReplace(Inf, "Agent###", "Agent***", ", ") Debug.Print SecretInf End Sub
Вот результаты ее работы:
Agent***, Agent***, Агент Майор Пронин, Agent***
Другим важным случаем, допускающим простую реализацию, является случай, когда замене подлежат вхождения отдельного символа, а не целой подстроки, как в общем случае. Наша функция производит замену разных символов из заданного множества на один и тот же символ. Алгоритм основан на обычном линейном просмотре и посимвольном сравнении. Сама замена производится путем выделения и конкатенации.
Эта функция понадобилась нам для реализации нового варианта функции расщепления строки, хотя она полезна и сама по себе. Приведем ее текст:
Public Function CharSetReplace(ByVal expr As String, ByVal find As String, _ ByVal Rep As String) 'Эта функция применима в том частном случае, когда в строке - источнике Expr 'требуется произвести замену символов, входящих в множество, заданное аргументом find 'на символ (последовательность символов), заданных аргументом rep Dim Source As String Dim i As Long, Sym As String Dim N As Long N = Len(expr) Source = expr For i = 1 To N Sym = VBA.Mid$(Source, i, 1) If InStr(1, find, Sym) Then Source = VBA.Left$(Source, i - 1) Rep VBA.Mid$(Source, i + 1) End If Next i CharSetReplace = Source End Function
Приведем теперь CharSetReplace:
Public Sub testCharSetReplace() Dim Text As String Text = "test123" Text = CharSetReplace(Text, "1234567890", "*") Debug.Print Text Text = "Шла кошка по крыше" Text = CharSetReplace(Text, "аоеиуыяюэ", "*") Debug.Print Text End Sub
В результате ее работы в окне отладки появляются следующие данные:
Шл* к*шк* п* кр*ш* Agent***, Agent***, Агент Майор Пронин, Agent***
Во многих ситуациях есть необходимость производить фильтрацию элементов массива, основываясь на шаблоне, которому должны удовлетворять фильтруемые элементы. Здесь есть полная аналогия с тем, что есть потребность производить замену на основе шаблона, а не точного образца. Поэтому естественно наше желание расширить стандартные возможности функции Filter, тем более, что реализуется эта возможность достаточно просто. Рассмотрим реализацию нашей функции:
Public Function WildFilter(sourcearray() As String, ByVal match As String, _ Optional ByVal include As Boolean = True) As Variant 'Эта функция фильтрует элементы исходного массива, проверяя их на соответствие шаблону match 'В отличие от стандартной функции Filter элементы проверяются на точное соответствие 'но шаблон match может включать специальные символы шаблона. 'Возвращается массив отобранных элементов. Если булев параметр include равен True 'то отбираются элементы, совпадающие с шаблоном, в противном случае - не совпадающие. Dim Sel() As String Dim i As Long, j As Long j = 1 If include Then For i = LBound(sourcearray) To UBound(sourcearray) If sourcearray(i) Like match Then ReDim Preserve Sel(1 To j) Sel(j) = sourcearray(i): j = j + 1 End If Next i Else For i = LBound(sourcearray) To UBound(sourcearray) If Not (sourcearray(i) Like match) Then Sel(j) = sourcearray(i): j = j + 1 End If Next i End If WildFilter = Sel End Function
Параметры этой функции сохраняют смысл стандартной функции Filter. Единственное отличие состоит в том, что теперь аргумент match задает настоящий шаблон, допускающий специальные символы. Чтобы быть справедливым, заметим, что теперь речь идет о точном соответствии шаблону, в то время, как в стандартной функции речь идет о вхождении шаблона в элемент. Так что обе функции имеют свои сферы применения. Реализация достаточно очевидна и основана на прямом использовании операции Like. Приведем тестовый пример, в котором вызывается наша функция:
Public Sub testWildFilter() Dim Txt As String, resTxt As String Dim Artxt() As String, ResAr() As String Txt = "123, 5, 117, 7, 189" Artxt = Split(Txt, ", ") ResAr = WildFilter(Artxt, "1##") resTxt = Join(ResAr, ", ") Debug.Print resTxt ResAr = WildFilter(Artxt, "[3-57-9]") resTxt = Join(ResAr, ", ") Debug.Print resTxt End Sub
Приведем результаты ее работы:
123, 117, 189 5, 7
Завершим обзор рассмотрения собственных инструментальных средств описанием функции WildSplit. Когда мы описывали стандартную функцию разбора строки на элементы, мы отмечали, что она, к сожалению, не применима в тех достаточно часто встречающихся ситуациях, когда элементы разделяются разными разделителями. Наша функция в какой то мере пытается восполнить существующий пробел. Она разделяет строку на элементы, которые могут в строке разделяться разными символами, представляющими разделители элементов, возможно, окруженные пробелами.
Алгоритм реализации прост. Он использует ранее написанную функцию замены CharSetReplace, заменяя все разделители одним. После этого становится возможным применить стандартную функцию разбора. На заключительном этапе у элементов убираются пробелы слева и справа. Следует отметить и недостаток этой функции, - преобразование необратимо и последующее применение Join не позволяет восстановить исходную строку в первозданном виде. Приведем текст этой функции:
Public Function WildSplit(expr As String, Optional ByVal delimiter As String = " ", _ Optional ByVal limit As Long = -1, Optional ByVal compare As VbCompareMethod = vbBinaryCompare) As Variant 'Также, как и стандартная функция Split, эта функция расщепляет строку - источник expr 'на элементы, используя разделители, заданные аргументом delimiter 'Отличие состоит в том, что при выделении элементов предполагается, что разделителем 'может быть любой из символов множества delimeter, возможно, окруженный пробелами Dim Source As String, ResAr() As String Dim find As String, Rep As String Dim i As Long Source = expr find = VBA.Mid$(delimiter, 2) Rep = VBA.Left$(delimiter, 1) 'Заменяем в строке все разделители на один из них Source = CharSetReplace(Source, find, Rep) 'Теперь используем стандартную функцию Split ResAr = Split(Source, Rep, limit, compare) 'Удаляем пробелы For i = LBound(ResAr) To UBound(ResAr) ResAr(i) = VBA.Trim$(ResAr(i)) Next i WildSplit = ResAr End Function
Приведем также WildSplit:
Public Sub testWildSplit() Dim Txt As String, Res As String Dim Items() As String Txt = "a * b - (c+d)/v" Items = WildSplit(Txt, "+-*/") Res = Join(Items) Debug.Print Res End Sub
Вот результаты ее работы:
a b (c d) v
На этом мы закончим наш затянувшийся разговор о работе со строковыми данными.
Для того, чтобы обеспечить программисту возможность корректно работать с датами и временем, VBA предоставляет Date, хранящий дату и время. Над данными этого типа можно выполнять некоторые операции, но, конечно же, при работе с ними чаще всего используются специальные встроенные функции. Попытаемся коротко рассмотреть основные возможности работы с датами. Прежде всего, заметим, что возможный диапазон дат охватывает даты от 1.1.100 года до 1-го января 9999 года. Если говорить о внутреннем представлении дат, занимающих 4 байта памяти, то целая часть хранит число дней от некоторой начальной даты, дробная часть хранит время от полуночи. Начальной датой является 30-е декабря 1899 года. Благодаря такому внутреннему представлению сложение и вычитание целого числа воспринимается как прибавление или вычитание дней.
При присваивании значений переменным # " или задавать ее как строковую константу. При задании даты в ограничителях, например #9, May, 99 # она автоматически преобразуется к стандартному формату #5/9/99# (месяц/день/год). Вот пример некоторых действий над датами:
Public Sub WorkWithDates() 'Работа с датами Dim dat1 As Date, dat2 As Date, dat3 As Date 'Присваивание дат dat1 = 12 dat2 = 9 / 5 / 99 dat3 = #9/5/1999# Debug.Print dat1, dat2, dat3 dat1 = "15/7/99" dat2 = #5/9/1999# dat3 = dat3 + 100 Debug.Print dat1, dat2, dat3 If dat3 > dat2 Then Debug.Print dat3 - dat2 Else Debug.Print dat2 - dat3 End If End Sub
Приведем результаты выполнения этой программы:
11.01.1900 0:26:11 05.09.99 15.07.99 09.05.99 14.12.99 219
Прокомментируем результаты, поскольку некоторые из них могут вызывать недоумение. Остановимся на первых двух присваиваниях. Чтобы понять их, нужно вспомнить внутренне представление дат. В первом случае к начальной дате прибавляется 12 дней, отсюда и получается 11 января 1900 года. Во втором случае, поскольку выражение в правой части не заключено в ограничители, оно вычисляется как обычное арифметическое выражение, полученное дробное число воспринимается как время от начала суток, - оно и печатается. Обратите внимание, на третье присваивание, первое число воспринимается как номер месяца, для российских программистов здесь кроется источник возможных ошибок, поэтому лучше задавать даты, используя названия месяцев, которые автоматически будут преобразованы в соответствующий формат.
Далее в программе продемонстрированы некоторые возможности выполнения операций над датами - прибавление целого числа дней, сравнение дат, вычитание дат. Заметьте, вычитание дат дает разницу между ними, выраженную в днях.
Как мы уже говорили, основная работа с датами выполняется с использованием встроенных функций. Рассмотрим коротко их описание и назначение.
Date - возвращает текущую дату.Time - возвращает текущее время по часам компьютера.Now - возвращает значение типа Variant (Date), содержащее текущую дату и время по системному календарю и часам компьютера.Функция DateAdd предназначена для добавления или вычитания указанного временного интервала из значения даты. Заметьте, с ее помощью можно задавать временной интервал не только в днях, но и в месяцах, годах и так далее.
DateAdd(interval, number, date)
Аргумент interval - строка, указывающая тип добавляемого временного интервала, number - число временных интервалов, на которое следует изменить дату, date - дата, к которой добавляется указанный временной интервал. Допустимые значения аргумента interval: приведены в следующей таблице.
| Значение | Описание |
|---|---|
| yyyy | Год. |
| Q | Квартал. |
| m | Месяц. |
| Y | День года. |
| D | День месяца. |
| w | День недели. |
| ww | Неделя. |
| H | Часы. |
| N | Минуты. |
| S | Секунды. |
Для примера, приведем два вызова этой функции в окне отладки:
? DateAdd("m", 1, "31-янв-95")
28.02.95
? DateAdd("m", -1, "31-янв-95")
31.12.94
Функция DateDiff предназначена для определения времени, прошедшего между двумя датами. Например, с помощью этой функции можно вычислить число дней между двумя датами или число недель между текущей датой и концом года. Синтаксис:
DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]])
interval задает тип временного интервала при вычислении разности между датами date1 и date2, - его возможные значения те же, что и для функции DateAdd,date1 и date2 - две даты, разность между которыми следует вычислить.firstdayofweek - константа, указывающая первый день недели (по умолчанию считается, что неделя начинается с воскресенья).firstweekofyear - константа, указывающая первую неделю года (по умолчанию первой неделей считается неделя, содержащая 1 января).Приведем примеры вызова этой функции в окне отладки:
? DateDiff("m", "18.10.55", "31-янв-95")
471
? DateDiff("Y", "18.11.97", "01.01.97")
-321
Функция DatePart предназначена для определения указанного компонента даты. Например, с помощью этой функции можно определить день недели или текущий час. Синтаксис:
DatePart(interval, date[,firstdayofweek[, firstweekofyear]])
interval задает тип возвращаемого временного интервала,date - дата, подлежащая обработке.Firstdayofweek и Firstweekofyear имеют тот же смысл, что и для функции DateDiff.Примеры вызова:
? DatePart("ww", "02.09.89")
35
? DatePart("w", "02.09.89")
7
Функция DateSerial позволяет вычислить значение даты типа Variant (Date) по ее компонентам, - году, месяцу и дню. Ее синтаксис:
DateSerial(year, month, day)
Значение каждого аргумента должно лежать в соответствующем диапазоне: 100 -- 9999 для года, 1 - 31 для дней и 1 - 12 для месяцев. Можно также использовать для аргументов числовые выражения для описания относительной даты.
Примеры:
? DateSerial(1997 - 25, 10 - 2, 18 - 1) 17.08.72 ? DateSerial(Year(Now) - 3,Month(Now) - 2, Day(Now) - 1) 08.03.96
Функция DateValue переводит аргумент-строку в дату. В отличие от функции преобразования CDate, функция DateValue правильно обрабатывает допустимые даты, содержащие полные или краткие названия месяцев.
Ее синтаксис:
DateValue(date)
Аргумент date может задавать как дату, так и время. Возможные разные форматы задания даты, в том числе и с названиями месяцев:
Примеры:
? DateValue ("25.04.1997")
25.04.97
? DateValue ("04.25.1997")
25.04.97
? DateValue ("25 апреля 1997")
25.04.97
? DateValue ("25 - апр.-97")
25.04.97
? DateValue ("Апрель, 25, 97")
25.04.97
? DateValue ("25/04/1997")
25.04.97
? DateValue ("25.04")
25.04.99
Последнее равенство связано с тем: что при отсутствии года, функция DateValue использует текущий год по системному календарю компьютера.
Функции Day(date), Month(date), и Year(date) являются, в некотором смысле, обратными к двум предыдущим. Они по аргументу-дате определяют номер дня, месяца и года. Функция Weekday(date, [firstdayofweek]) возвращает значение типа Variant (Integer), содержащее целое число, представляющее день недели. Второй аргумент задает первый день недели (по умолчанию - воскресенье ). Значение 2 соответствует понедельнику.
Функции Hour(время), Minute(время) и Second(время) по аргументу, являющемуся числовым или строковым выражением, представляющим время, возвращает содержащее целое число, которое представляет, соответственно, часы, минуты и секунды в значении времени.
Функция TimeSerial(hour, minute, second) вычисляет результат Variant (Date), содержащее значение времени, соответствующее указанным часу, минуте и секунде.
Функция TimeValue(время) возвращает значение типа Variant (Date), содержащее время. Аргумент время обычно задается строковым выражением, представляющим время от 0:00:00 (12:00:00 A.M.) до 23:59:59 (11:59:59 P.M.) включительно. Задавать его нужно корректно, чтобы не возникало ошибок, как в последних двух примерах:
? Year(Now)
1999
? Month(now)
5
? Day(Now)
9
? Hour(Now)
11
? Minute (Now)
57
? Second(Now)
28
? TimeSerial(Hour(Now), Minute(now), Second(Now))
11:59:00
? TimeValue("12:30 PM")
12:30:00
? TimeValue("12.30")
0:00:00
? TimeValue("12,5")
0:00:00
Функция Timer возвращает значение типа Single, представляющее число секунд, прошедших после полуночи. Эта функция широко используется при проведении хронометража вычислений в программах. Чтобы повысить эффективность выполнения VBA программ зачастую приходится прибегать к проведению хронометража, чтобы выявить наиболее критичные по времени выполнения участки программ, а затем уже применять специальные меры для ускорения вычислений. Приведем сейчас пример, в котором анализируется время, затрачиваемое для выполнения операций над арифметическими данными разных подтипов.
Public Sub Speed() 'Эта программа выполняет замеры времени вычислений над данными разного типа Dim Start As Single, Finish As Single Dim i As Long, j As Long Dim bx As Byte, by As Byte, bz As Byte Dim ix As Integer, iy As Integer, iz As Integer Dim lx As Long, ly As Long, lz As Long Dim sx As Single, sy As Single, sz As Single Dim dx As Double, dy As Double, dz As Double For i = 1 To 5 'Внешний цикл для повторения замеров времени Start = Timer For j = 1 To 100000 bx = 99: by = 101 bz = by * (by - bx) \ by Next j Finish = Timer Debug.Print "Тип Byte: Время ", i, " = ", Finish - Start Debug.Print "bz = ", bz Start = Timer For j = 1 To 100000 ix = 99: iy = 101 iz = iy * (iy - ix) \ iy Next j Finish = Timer Debug.Print "Тип Integer: Время ", i, " = ", Finish - Start Debug.Print "iz = ", iz Start = Timer For j = 1 To 100000 lx = 99: ly = 101 lz = ly * (ly - lx) \ ly Next j Finish = Timer Debug.Print "Тип Long: Время ", i, " = ", Finish - Start Debug.Print "lz = ", lz Start = Timer For j = 1 To 100000 sx = 99: sy = 101 sz = sy * (sy - sx) / sy Next j Finish = Timer Debug.Print "Тип Single: Время ", i, " = ", Finish - Start Debug.Print "sz = ", sz Start = Timer For j = 1 To 100000 dx = 99: dy = 101 dz = dy * (dy - dx) / dy Next j Finish = Timer Debug.Print "Тип Double: Время ", i, " = ", Finish - Start Debug.Print "dz = ", dz Next i End Sub
Представим результаты вычислений в виде таблицы. Они представляют интерес сами по себе. Заметьте, что наибольшее время потребовалось для вычислений над самым коротким типом - Byte, а в целом вычисления над разными типами выполняются примерно с одинаковой скоростью.
| Тип / Номер Эксперимента | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
Byte |
0.3515625 | 0.359375 | 0.3320313 | 0.3320313 | 0.34375 |
Integer |
0.2421875 | 0.2382813 | 0.2421875 | 0.2421875 | 0.2382813 |
Long |
0.2890625 | 0.28125 | 0.28125 | 0.28125 | 0.28125 |
Single |
0.2929688 | 0.2695313 | 0.28125 | 0.28125 | 0.28125 |
Double |
0.3085938 | 0.2890625 | 0.2929688 | 0.2929688 | 0.28125 |
Как мы уже говорили, встроенных функций множество. Помнить их всех не требуется, поскольку под рукой всегда есть справочная система. Для общего знакомства рассмотрим еще некоторые группы функций.
К этой группе относится функция TypeName, которая по имени переменной возвращает значение типа String, представляющее ее тип.
Вызов имеет вид:
TypeName(имяПеременной)
где параметр имяПеременной представляет выражение типа Variant, определяющее любую переменную, за исключением переменной с определяемым пользователем типом.
Строка, возвращаемая функцией TypeName, может быть любой из следующих: Byte, Integer, Long, Single, Double, Currency, Decimal, Date, String, Boolean, Error, Empty (если переменная не инициализирована), Null, Object, Unknown (тип неизвестен), Nothing (объектная переменная, не содержащая ссылки на объект).
Для проверки типа имеется также набор функций с булевыми значениями. Они применяются к переменным или выражениям (часто имеющим тип Variant ) и определяют, имеют ли те заданный тип или значение. В следующей таблице перечислены функции этой группы и указаны определяемые ими типы.
| Функция | Что проверяет |
|---|---|
IsArray(переменная) |
Является ли переменная массивом. |
IsDate(выражение) |
Может ли значение выражения быть преобразовано в значение даты. |
|
Была ли инициализирована переменная. |
IsError(выражение) |
Представляет ли выражение значение ошибки. |
IsNull(выражение) |
Является ли результатом выражения пустое значение ( Null ). |
IsNumeric(выражение) |
Имеет ли выражение числовое значение. (Для выражений False ). |
IsObject(переменная) |
Представляет ли переменная объектную переменную. Возвращает True, даже для переменной со значением Nothing. |
Для приведения данных к нужному типу в VBA включен обширный набор функций: CBool, CByte, CCur, CDate, CDbl, CDec, CInt, CLng, CSng, CStr, CVar, CVErr, Fix, Int.
Все они имеют следующий синтаксис:
ИмяФункции(выражение)
Обязательный аргумент выражение является любым строковым выражением или числовым выражением. В следующей таблице указаны типы возвращаемых значений и их диапазоны.
| Функция | Тип | Диапазон аргумента выражение |
|---|---|---|
CBool |
Boolean |
Любая допустимая строка или числовое выражение. Например, Cbool(0)= Cbool("00")= False, Cbool(1)=Cbool("-6")=True. |
CByte |
Byte |
От 0 до 255. |
CCur |
Currency |
От -922 337 203 685 477,5808 до 922 337 203 685 477,5807.Например, CCur("25,878755")= 25.8788, а CCur("25.878755") - ошибочный вызов, т. к. в строке отделять дробную часть должна запятая. |
CDate |
Date |
Любое допустимое выражение даты. Например, Cdate(32444.5)=28.10.88 12:00:00, Cdate("17/08/1972")=17.08.72, Cdate("6:45:28 PM")= 18:45:28. |
CDbl |
Double |
-4,94065645841247E-324 для отрицательных чисел; от 4,94065645841247E-324 до 1,79769313486232E308 для положительных чисел. |
CDec |
Decimal |
+/-7,9228162514264337593543950335. Минимальное ненулевое число 0,0000000000000000000000000001. |
CInt |
Integer |
От -32 768 до 32 767 с округлением дробной части. |
CLng |
Long |
От -2 147 483 648 до 2 147 483 647 с округлением дробной части. |
CSng |
Single |
От -3,402823E38 до -1,401298E-45 для отрицательных чисел; от 1,401298E-45 до 3,402823E38 для положительных чисел. |
CVar |
Variant |
Диапазон значений Double для числовых значений. Диапазон значений String для нечисловых значений. |
CStr |
String |
Возвращаемые значения функции CStr зависят от аргумента выражение. Например, CStr(True) = "Истина", CStr(122.344) = "122.344", CStr(#10/24/47#) = 24.10.47 |
Если переданное в функцию значение аргумента выражение находится вне допустимого диапазона для соответствующего типа данных, возникает ошибка.
Функция CVErr возвращает значение типа Variant с подтипом Error, содержащее код ошибки, указанный пользователем. Она используется для создания определяемых пользователем ошибок.
Если дробная часть числа равна 0,5, то функции CInt и CLng всегда округляют число до ближайшего четного числа. Например, Cint(0,5)= 0, а Cint(1,5)= 2.
Функции Fix и Int вычисляют целую часть числа (без округления). Они отличаются на отрицательных числах: Fix (-7.6) = -7, а Int(-7.6) = -8.
Числовые и строковые данные, данные
Функция возвращает строку, отформатированную в соответствии с указаниями, заданными при вызове. Ее синтаксис:
Format(expression[, format[, firstdayofweek[, firstweekofyear]]])
Ее параметры:
expression - любое правильное выражение.Format - имя встроенного параметра или определение пользовательского формата. Если параметр опущен, то применяется формат, зависящий от типа первого аргументаПриведем ряд примеров, в которых используется форматирование чисел на основе форматов, определяемых по умолчанию и пользователем:
? VBA.Format(55) 55 ? VBA.Format(5.5) 5,5 ? VBA.Format(-52.125, "##0.#0") -52,13 ? VBA.Format(-52.125, "000.##0") -052,125 ? VBA.Format(1152.125, "#,##0.#0") 1 152,13 ? VBA.Format(0.52125, "0.##0%") 52,125%
Несколько примеров форматирования строк:
? VBA.Format("5.4")
05.04.99
? VBA.Format("5,4")
5,4
? VBA.Format("Мария", ">")
МАРИЯ
? VBA.Format("Мария", "<")
мария
? VBA.Format("Мария", "> Это ")
Это МАРИЯ
Даты форматируются обычно с использованием встроенных форматов, но можно применять и собственные определения форматов. Вот несколько примеров:
? VBA.Format(VBA.Time, "Long Time") 16:24:57 ? VBA.Format(VBA.Time, "Short Time") 16:25 ? VBA.Format(VBA.Time, "hh/mm/ss") 16.26.03 ? VBA.Format(VBA.Date, "Long Date") 9 Май 1999 г. ? VBA.Format(VBA.Date, "Short Date") 09.05.99 ? VBA.Format(VBA.Date, "yy/mm/dd") 99.05.09
Функция Format является общей функцией применимой к произвольным выражениям. Остальные функции применимы к выражениям специального типа, предоставляя некоторые дополнительные возможности. Мы не станем их подробно рассматривать, ограничившись простым перечислением:
FormatCurrency - возвращает денежное выражение, используя денежный знак.FormatDataTime - возвращает дату или время.FormatNumber - возвращает выражение, отформатированное как число.FormatPercent - возвращает выражение, заданное в процентах, с указанием знака процента.Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: DocOne8, DocTwo8.
В любом языке программирования допустимы выражения. Нужно уметь выражаться корректно. Выражения строятся из переменных, констант, встроенных функций с использованием знаков операций и скобок. Запись выражения задает правило (алгоритм) вычисления его значения и его типа. Естественно, что тип и значения всех его переменных должны быть определены до момента вычисления выражения. Языки программирования различаются между собой тем, до какой степени они допускают автоматическое X+Y вещественный тип для переменной X и целочисленный тип для переменной Y, проводя автоматическое преобразование к вещественному типу и выполняя, затем уже, сложение вещественных чисел. Язык VBA в этом отношении занимает золотую середину. Он не столь строг, как классические языки, предложенные Никласом Виртом - Паскаль, Модула, Оберон, но и не допускает особых вольностей. Среди его встроенных функций есть большое число функций, предназначенных для явного преобразования типов, что позволяет программировать в лучших традициях надежных языков программирования, не доверяя
Тема построения выражений обширна, достаточно сказать, что число встроенных функций измеряется не десятками, а сотнями. Подробное их описание не входит в нашу задачу, для этого есть справочная система, но общий обзор и некоторые детали мы постараемся привести.
Приведем основные операции, которые можно выполнять над данными в языке VBA, классифицируя их по типу и приоритету:
| Приоритет | Арифметические | Сравнения | Логические | Описание некоторых операций |
|---|---|---|---|---|
| 1 | Возведение в степень - (^) | Равенство - (=) | Отрицание - (Not) | При возведении в степень основание и показатель могут быть арифметическими выражениями любого типа. Результат имеет тип Double. |
| 2 | Унарный минус - (-) | Неравенство - (<>) | Конъюнкция - (And) | |
| 3 | Умножение, Деление - (*, /) | Меньше - (<) | Дизъюнкция - (Or) | |
| 4 | Деление нацело - (\) | Больше - (>) | Исключительное Или - (Xor) | Деление нацело определено над целочисленными данными (применимо и к вещественным данным) и дает результат целого типа. Исключительное Или требует, чтобы один из операндов имел значение, отличное от True |
| 5 | Остаток от деления нацело - (mod) | Меньше или равно - (<=) | Эквивалентность - (Eqv) | Операция mod определена над данными целого типа и возвращает результат целого типа - остаток от деления нацело. |
| 6 | Сложение, вычитание - (+,-) | Больше или равно - (>=) | Импликация - ( |
Среди логических операций определена операция следования (импликация), ложная в единственном случае, когда посылка истинна, а заключение ложно. |
| 7 | Конкатенация строк - () | Подобия - ( Like ), Равенство ссылок - ( Is ) |
Операция Like проверяет соответствие строки образцу. Операция Is, определенная над объектами, не проверяет равенство самих объектов, она проверяет совпадение ссылок. Ссылки должны задавать один и тот же объект. |
Если выражение содержит операции разных категорий, то первыми выполняются арифметические операции, затем, операции сравнения и последними - логические.
Все операции сравнения имеют один и тот же приоритет. Арифметические и логические операции выполняются в соответствии с указанным приоритетом.
Одна и та же операция, записанная несколько раз подряд, или операции одного приоритета (умножение и деление, сложение и вычитание) выполняются слева направо, - из двух операций первой выполняется та, которая стоит левее в записи выражения.
Скобки позволяют изменить указанный порядок вычислений выражения, поскольку выражения в скобках имеют наивысший приоритет и вычисляются первыми. Внутри скобок действует обычный порядок вычисления.
Операция конкатенации не является арифметической, но при ее появлении в выражении она выполняется после всех арифметических операций, но до вычисления операций сравнения.
Арифметика в VBA представлена достаточно полно, Напомним, что арифметический тип подразделяется на подтипы:
Byte, Integer, Long - для представления целочисленных данных.Single, Double - для представления вещественных данных.Decimal - для представления чисел в форме с фиксированной точкой, что важно, в частности, для финансовых вычислений.Currency - специальный тип для представления денежных данных.Variant - обобщенный тип, позволяющий хранить и обрабатывать данные разного типа.Возможные арифметические операции мы уже упомянули.
Пример работы с числовыми данными:
Public Sub WorkWithArithmetic() Dim X As Integer, Y As Integer Dim U As Single, V As Single Dim Z As Double U = 15.8: V = -6.5 Z = U / V: X = CInt(U / V): Y = U \ V Debug.Print X, Y, Z, U, V, X \ Y, X Mod Y, U Mod V End Sub
Вот результаты печати в окне отладки:
-2 -2 -2,43076926011306 15,8 -6,5 1 0 4
Заметим, что хотя целочисленные операции возможны над вещественными данными, применять их не следует, поскольку это один из тех случаев, когда выполняются внутренние преобразования, точная интерпретация которых сложна, что и может, в конечном итоге, служить источником программистских ошибок.
Рассмотрим основные встроенные математические функции.
Набор математических функций VBA достаточно стандартный. Перечислим их с краткими пояснениями:
Abs(число) - абсолютное значение числа.Atn(число) - арктангенс (в радианах) аргумента, задающего тангенс угла.Cos(число) - косинус угла. Аргумент число задает угол в радианах.Exp(число) - экспонента, т. е. результат возведение числа e (основание натуральных логарифмов) в указанную степень.Log(число) - натуральный логарифм числа.Rnd[(число)] - результат представляет равномерно распределенное случайное число в интервале [0 - 1]. Если аргумент число не задан или больше нуля, то порождается очередное случайное число, если он равен 0, то результатом будет предыдущее случайное число, а если число меньше нуля, то всякий порождается одно и то же число, определяемое аргументом. Перед тем, как получить последовательность случайных чисел необходимо вызвать функцию Randomize для инициализации последовательности. Заметим, для формирования значения случайных чисел используется таймер. Чтобы получить целочисленную последовательность равномерно распределенных случайных чисел в интервале [Min - Max], следует использовать следующее преобразованиеInt((Max - Min +1)*Rnd)+ Min
В примерах предыдущей лекции мы неоднократно использовали эту функцию для инициализации массивов с числовыми данными.
Sgn(число) - знак числа (Если число больше нуля - 1, равно нулю - 0, меньше нуля - -1 ).Sin(число) - синус угла. Аргумент число задает угол в радианах.Sqr(число) - квадратный корень.Tan(число) - тангенс угла. Аргумент число задает угол в радианах.Во всех этих описаниях под аргументом функции число понимается числовое выражение.
В справочной системе или в любом математическом справочнике Вы сможете найти указания, как на основе этого базиса, можно вычислить большое число других математических функций. Например, вычисление гиперболического синуса производится по формуле:
HSin(X) = (Exp(X) - Exp(-X))/2
а вычисление арккотангенса:
Arccotan(X) = Atn(X) + 2*Atn(1)
Работать с текстами программисту, как правило, приходится значительно чаще, чем работать с числами. Поэтому следует хорошо представлять основные базисные операции над строками, которые в большинстве случаев реализуются с помощью встроенных функций. Заметим, что в VBA Office 2000 добавлены новые операции над строками, расширяющие возможности эффективной работы по преобразованию текста.
Обычные операции сравнения применимы и к строковым данным. Мы уже говорили ранее о том, что интерпретация этих операций зависит от установки опции Option Compare.
Text, то сравнение на "больше - меньше" представляет лексикографическое сравнение, когда строки сравниваются по их расположению в словаре. Заметьте, это сравнение не чувствительно к регистру, так что большие и малые буквы не различаются. Программисты, конечно, понимают, что сравнение строк означает сравнение кодов их символов, так что Binary, то сравнение идет побитно. В этом случае сравнение естественно, чувствительно к регистру.Если нужно локально переопределить вид сравнения, заданный опцией для всего модуля, то можно использовать встроенную функцию StrComp, которая возвращает результат сравнения строк. Ее синтаксис:
StrComp(string1, string2[, compare])
Аргументы string1 и string2 - сравниваемые строки. Необязательный аргумент compare указывает способ сравнения строк: значение по умолчанию 0 используется, чтобы выполнить двоичное сравнение, 1 задает посимвольное сравнение без учета регистра.
Если string1 меньше чем string2, то результат равен -1, если строки равны, то - 0, если вторая меньше, то - 1, если хоть одна из строк имеет значение Null, то результат также равен Null.
Мощным и весьма полезным средством при работе с текстами является операция Like, задающая сравнение с образцом. Необходимость нахождения в наборе всех строк, удовлетворяющих некоторому шаблону (образцу), возникает в самых разнообразных задачах. VBA позволяет решать ее в одну операцию. Приведем таблицу специальных символов, допустимых при задании образца.
| Символы | Интерпретация | Примеры |
|---|---|---|
* |
Любой текст - произвольное число символов | Шаблону Agent* соответствуют все тексты, начинающиеся со слова Agent. Строки Agent007 и Agent Майор Пронин удовлетворяют шаблону. |
| ? | Один любой символ | Шаблону К?к удовлетворяют, в частности строки Кок и Кук. |
# |
Любая цифра от 0 до 9 | Шаблону Agent### соотвествуют 1000 различных строк, среди которых и Agent007, но, конечно же, не Agent Майор Пронин. |
[множество_символов] |
Любой символ, принадлежащий множеству | Задать множество можно с помощью перечисления и интервалов. Шаблону К[аоу]к удовлетворяют слова "Как", "Кок", "Кук". Чувствительность к регистру зависит от установки опции Option Compare. |
[!множество_символов] |
Любой не принадлежащий множеству символ | Шаблону [!а-я] удовлетворяет символ, не являющийся буквой русского алфавита. |
Приведем пример работы с операцией Like:
Public Sub LikeOperation() Const pat1 = "[A-Z]" Const pat2 = "[a-z]" Const pat3 = "[!a-z]" Const pat4 = "[3-5]" Dim res As Byte Dim Sym As String res = "Кук" Like "К[аоу]к" Debug.Print res res = "f" Like pat1 Debug.Print res res = "f" Like pat2 Debug.Print res res = "f" Like pat3 Debug.Print res res = "5" Like pat4 Debug.Print res Sym = "3" res = Sym Like pat1 pat4 Debug.Print res res = Sym Like pat1 Or Sym Like pat4 Debug.Print res End Sub
Вот результаты отладочной печати:
255 0 255 0 255 0 255
Обратите внимание на последние два результата, демонстрирующие некорректный и корректный способы работы с объединением множеств проверяемых символов.
В классической математике давным - давно определен набор основных операций над числовыми и булевыми данными. Строковой арифметикой серьезно стали заниматься с появлением компьютеров. Жесткий стандарт на эти операции еще не сложился. Как правило, во всех языках есть только одна операция, называемая конкатенацией строк, обозначаемая, обычно, символом "". Все остальные основные операции реализуются с помощью встроенных функций, имена которых и их аргументы могут варьироваться от языка к языку. Более того, варьируется и сам набор этих операций. Минимально, помимо конкатенации необходимы еще две операции, первая из которых позволяет обнаружить индекс вхождения одной строки в другую, вторая - выделить из строки ее подстроку. VBA имеет достаточно мощный набор операций, который в Office 2000 существенно был расширен принципиально новыми возможностями. Рассмотрим вначале те операции, которые и ранее существовали в языке, а уж потом, чуть более подробно поговорим о новых возможностях.
Функция Len(string) возвращает длину строки, заданной аргументом String. Заметьте, возвращается число символов строки, а не число байтов. Ранее мы уже говорили, что в Office 2000 для внутреннего представления строк используется Unicode кодировка, в которой каждый символ занимает два байта.
Функция InStr определяет позицию (индекс) первого вхождения одной строки внутри другой строки. Синтаксис
InStr([start, ]string1, string2[, compare])
Необязательный аргумент start задает позицию, с которой начинается поиск (по умолчанию - с первого символа строки). Аргумент string1 задает строку, в которой выполняется поиск, а string2 - подстроку, вхождение которой ищется. Необязательный аргумент compare имеет тот же смысл, что и для функции StrComp. Возвращаемые значения определяются следующей таблицей.
| Условия | Значение функции InStr |
|---|---|
string1 - пустая строка |
0 |
string1 или string2 равны Null |
Пустое значение - Null |
string2 - пустая строка |
start |
Вхождение string2 не найдено в string1 |
0 |
Вхождение string2 найдено в string1 |
Позиция обнаруженной подстроки |
start >Len(string2) |
0 |
Например, два вызова этой функции в окне отладки вернут следующие результаты:
? InStr(4, "XXпXXпXXПXXП", "П", 1) 6 ? InStr(4, "XXпXXпXXПXXП", "П", 0) 9
Функция Left( выделяет в строке string указанное число length символов слева, позволяя выделить префикс строки.
Функция Right( выполняет аналогичную операцию, выделяя символы справа, что позволяет получить суффикс (окончание) строки.
Более Mid(string, start[, length]) позволяет выделить из строки string подстроку длины length, начиная с позиции start.
Вот пример вызова этих функций непосредственно из окна отладки:
? VBA.Left("рококо",3)
рок
? VBA.Right("рококо",3)
око
? VBA.Mid("рококо",3,3)
кок
Функции LTrim(string), RTrim(string), Trim(string) возвращают копию строки, из которой удалены пробелы, находившиеся в начале строки ( LTrim ), в конце строки ( RTrim ) или в начале и конце строки ( Trim ).
Функция String создает строку: содержащую заданное число повторяющихся символов.
Синтаксис:
String(number, character)
Аргумент number задает длину строки, а character - код символа или строковое выражение, первый символ которого используется при создании результирующей строки.
Функции LCase(string) и UCase(string) возвращают копию строки, символы которой приведены к нижнему ( Low ) или верхнему регистру ( Upper ).
Функции, возвращающие строки, существуют в двух вариантах, отличающихся именами. Функции, имена которых мы приводили, возвращают результат типа Variant. Во втором варианте имена функций оканчиваются знаком $, например, Mid$, UCase$. В этом случае результат возвращается типа String. Такие функции выполняются быстрее, но не могут корректно работать со строками, имеющими значение Null.
Приведем в качестве примера полезную функцию, которая находит путь к активному документу Word, и производит разбор всех компонент этого пути:
Public Function AppPath(Disk As String, Dir As String, FileName As String) As String 'Эта Функция возвращает в качестве результата полный путь активного документа 'Ее параметры содержат компоненты этого пути - имя диска, каталог на диске и имя файла Dim MyDoc As Document Dim Path As String Dim Start As Byte, Finish As Byte 'Определяем полный путь к файлу, задающему активный документ Word Set MyDoc = ActiveDocument Path = MyDoc.FullName 'Выделяем имя диска - первый символ полного пути Disk = VBA.Left(Path, 1) 'Выделяем каталог, в котором хранится документ Start = VBA.InStr(1, Path, "\") Finish = VBA.InStrRev(Path, "\") Dir = VBA.Mid(Path, Start + 1, Finish - Start) 'Выделяем имя файла FileName = VBA.Mid(Path, Finish + 1) 'Возвращается результат - полный путь к каталогу AppPath = VBA.Left(Path, Finish) End Function Public Sub MyPath() Dim Path As String Dim Dir As String Dim Disk As String Dim FileName As String Path = AppPath(Disk, Dir, FileName) Debug.Print Disk, Dir, FileName, Path End Sub
Вот результаты отладочной печати после запуска процедуры MyPath:
E O2000\VBA2000\Ch8\ Ch8.doc E:\O2000\VBA2000\Ch8\
Обратите внимание, в функции AppPath мы использовали новую функцию InStrRev, что облегчило решение нашей задачи. К описанию вновь введенных функций мы и переходим.
В VBA 2000, как мы уже говорили, добавлены полезные функции для работы со строками.
Функция InStrRev симметрично дополняет функцию InStr, аналогично тому, как функция Right дополняет функцию Left. Эта функция ищет вхождение подстроки в строку, но начинает свою работу с правого конца строки. Ее использование может существенно ускорить работу, если заранее известно, что искомая подстрока находится где- то в конце строки - источника. Если вхождение искомой подстроки единственно, то обе функции дают один и тот же результат. При множественном вхождении функция InStr возвращает первое вхождение, в то время как InStrRev - последнее. Ее синтаксис:
InstrRev(stringcheck, stringmatch[, start[, compare]])
Ее параметры имеют тот же смысл, что и у функции InStr, но, заметьте, порядок их задания изменен. Необязательный параметр Start теперь задается третьим по счету. Когда он опущен, то по умолчанию, его значение равно "-1", и поиск начинается с последнего символа. Если вернуться к нашему последнему примеру, то для решения нашей содержательной задачи требовалось определить первое и последнее вхождение символа "\" в строке, задающей путь к файлу. Поэтому обе функции были весьма кстати. Нужно отметить, что это весьма типичная ситуация при разборах текста.
Это удивительно, что в наборе встроенных функций не было до сих пор функции Replace. Замена одной подстроки на другую - это одна из основных операций при работе со строками. Поэтому в арсенале практически каждого из программистов была своя версия функции Replace. Теперь можно пользоваться стандартной реализацией. Ее несомненным достоинством является то, что она позволяет заменить не только первое вхождение искомой подстроки, но и все такие вхождения, не требуя организации цикла. С другой стороны, можно ограничиться только заменой первого или нескольких первых вхождений. Рассмотрим синтаксис появившейся функции:
Replace(expression, find, replace[, start[, count[, compare]]])
Первый аргумент expression задает строковое выражение, результат которого определяет строку - источник, в которой осуществляется замена. Аргументы find и Replace задают заменяемую подстроку и ее новое значение. Аргумент Count определяет число замен. Обычно он равен 1, когда речь идет о замене первого вхождения, или опускается, - в этом случае его значение по умолчанию равно " -1 ", означающее замену всех вхождений. Аргумент compare имеет обычный смысл.
В качестве примера приведем функцию Rep, которую мы постоянно используем в нашей работе в процессе работы над этой книгой. Все примеры в нашей книге проверены в реальной работе, они копируются из модулей VBA. При копировании программного текста в документ Word появляются последовательности пробелов, отражающие структурную запись процедур и функций. Но появление в документах Word двух пробелов подряд считается смертельным грехом. Заменять пробелы табуляцией можно и руками, но, конечно же, лучше написать макрос, решающий эту и другие задачи по приведению текста в форму, требуемую редакцией. Вот текст функции, используемой для этих целей:
Public Sub Rep() 'Эта процедура преобразует выделенный программный текст 'Заменяя пробелы табуляцией и конец абзаца мягким концом строки Dim TxtRange As String TxtRange = Selection.Range.Text 'Замена пробелов: 4-х, 3-х и 2-х символом табуляции TxtRange = Replace(TxtRange, " ", vbTab) TxtRange = Replace(TxtRange, " ", vbTab) TxtRange = Replace(TxtRange, " ", vbTab) 'Замена концов абзаца Selection.Range.Text = Replace(TxtRange, VBA.Chr(13), VBA.Chr(11)) End Sub
Несмотря на всю полезность функции Replace нам пришлось написать еще одну собственную модификацию этой функции. Дело в том, что у Replace есть одна особенность, на которую следует обратить внимание, - возвращаемый ею результат, начинается не с первой позиции, а с позиции, заданной аргументом Start. Так что помимо своей основной роли она еще обрубает голову строки, если только Start, отличается от 1. Поскольку замену зачастую нужно производить не с самого начала, а получать хочется полную строку, то мы написали свой вариант этой функции. Приведем его текст:
Public Function MyReplace(ByVal Expr As String, ByVal find As String, _ ByVal rep As String, Optional ByVal start As Long = 1, Optional ByVal count As Long = -1, _ Optional ByVal compare As VbCompareMethod = vbBinaryCompare) As String 'Вызов стандартной функции Replace If start = 1 Then MyReplace = replace(Expr, find, rep, start, count, compare) Else MyReplace = VBA.Left(Expr, start - 1) replace(Expr, find, rep, start, count, compare) End If End Function
Вот результаты нескольких вызовов Replace и MyReplace в окне отладки:
? Replace("A+B *(D*B +B)","B","C",4,1)
*(D*C +B)
? MyReplace("A+B *(D*B +B)","B","C",4,1)
A+B *(D*C +B)
? MyReplace("A+B *(D*B +B)","B","C",4)
A+B *(D*C +C)
Удаление вхождений подстроки в строку также является одной из основных операций над строками. Хотя специальной встроенной функции DelStr не появилось, но написать ее реализацию, имея Replace, совсем просто. Удалить подстроку эквивалентно замене ее пустой подстрокой. Вот текст, написанной нами функции DelStr:
Public Function DelStr(ByVal Expr As String, ByVal find As String, _ Optional ByVal start As Long = 1, Optional ByVal count As Long = -1, _ Optional ByVal compare As VbCompareMethod = vbBinaryCompare) 'Вызов функции MyReplace с пустой строкой для замены DelStr = MyReplace(Expr, find, "", start, count, compare) End Function
Приведем результаты ее вызовов в окне отладки:
? DelStr("T* + U** - V*","*")
T + U - V
? DelStr("T* + U** - V*","*",5)
T* + U - V
? DelStr("T* + U** - V*","*",5,1)
T* + U* - V*
Заметьте, при определении DelStr мы предпочли пользоваться собственной модификацией MyReplace, поскольку, как нам кажется, она в большей степени отвечает сути дела.
Важную группу новых функций составляют функции, предназначенные для разбора текста. Их появление позволяет эффективно решать целый класс часто встречающихся задач при работе с текстами. Суть дела такова: текст, заданный строкой, зачастую, представляет совокупность структурированных элементов - абзацев, предложений, слов, скобочных выражений и так далее. При работе с таким текстом необходимо разделить его на элементы, пользуясь тем, что есть специальные разделители элементов, - это могут быть пробелы, скобки, знаки препинания. Практически подобная задача возникает постоянно при работе со структурированными текстами. Новые функции существенно облегчают решение подобных задач. Функция Split позволяет разделить строку на элементы и создать массив из этих элементов. Функция Filter позволяет выделить нужные элементы в этом массиве, а функция Join решает обратную задачу, преобразуя массив в строку. Рассмотрим эти функции подробнее.
Предполагается, что исходная строка состоит из элементов (подстрок), разделенных специальными символами - разделителями. Функция Split возвращает одномерный массив из элементов строки. Ее синтаксис:
Split(expression[, delimiter[, limit[, compare]]])
Ее параметры:
expression - строковое выражение, результат которого задает строку - источник, состоящую из элементов и разделителей.delimeter - строка, задающая последовательность символов, используемых в качестве разделителя. Если этот параметр опущен, то по умолчанию предполагается, что в роли разделителя выступает пробел.limit - необязательный параметр, позволяющий ограничить число возвращаемых элементов. По умолчанию его значение равно -1, означающее выделение всех элементов.compare - необязательный параметр, имеющий стандартный смысл во всех операциях над строками. По умолчанию предполагается двоичное побитовое сравнение.Приведем пример, в котором сложноподчиненное предложение разделяется на простые предложения:
Public Sub SplitString()
'В этой процедуре сложное предложение разделяется на простые
'Объявляем динамический массив
Dim Simple() As String, i As Byte
'Размерность массива Simple устанавливается автоматически
'в соответствии с размерностью массива, возвращаемого функцией Split
Simple = Split("А это пшеница, которая в темном чулане хранится в доме, " _
"который построил Джек", ", ")
For i = LBound(Simple) To UBound(Simple)
Debug.Print Simple(i)
Next i
End Sub
В результате работы этой процедуры создается массив из трех элементов:
А это пшеница которая в темном чулане хранится в доме который построил Джек
Обратите внимание, для использования результатов работы функции Split создается Split, он заполняется элементами и у него появляется нижняя и верхняя граница. Далее с этим массивом можно работать обычным образом.
У функции Split, на наш взгляд, есть один недостаток. Хотя в роли разделителя может выступать последовательность символов, в нашем примере это пара символов "запятая, пробел", но нельзя использовать разные разделители элементов. В задачах подобного рода типичной является ситуация, когда элементы отделяются разными разделителями, возможно, окруженными пробелами. Позже мы покажем, как можно справиться с этой задачей.
Функция Join решает обратную задачу, - она восстанавливает строку по ее элементам, хранящимся в массиве, добавляя разделители в момент их объединения. Ее синтаксис:
Join(sourcearray[, delimiter])
Параметры ее понятны без особых пояснений. Заметим, что если необязательный аргумент опущен, то элементы разделяются пробелами. В качестве примера, приведем обратную сборку сложного предложения:
Public Sub SplitAndJoin()
'В этой процедуре сложное предложение разделяется на простые
'А затем после обработки строка восстанавливается
'Объявляем динамический массив
Dim Simple() As String, i As Byte
Dim Simple1() As String, Res As String
'Размерность массива Simple устанавливается автоматически
'в соответствии с размерностью массива, возвращаемого функцией Split
Simple = Split("А это пшеница, которая в темном чулане хранится в доме, " _
"который построил Джек", ", ")
'Создаем новый массив
ReDim Simple1(1 To UBound(Simple) + 2)
Simple1(1) = "А это веселая птица - синица"
Simple1(2) = "которая часто ворует пшеницу"
For i = 3 To UBound(Simple1)
Simple1(i) = Simple(i - 2)
Next i
'Создаем строку из массива Simple1
Res = Join(Simple1, ", ")
Debug.Print Res
End Sub
Вот результат отладочной печати:
А это веселая птица - синица, которая часто ворует пшеницу, которая в темном чулане хранится в доме, который построил Джек
Функция Filter является одним из вариантов поиска по образцу среди элементов массива. Здесь не требуется точного совпадения элемента и образца, достаточно, чтобы строка - образец содержалась в строке, заданной элементом массива. Поскольку совпадений может быть достаточно много, то результатом является массив отфильтрованных элементов. Некоторые детали будут пояснены при описании аргументов функции Filter. Вот ее синтаксис:
Filter(sourcearray, match[, include[, compare]])
Ее параметры имеют следующий смысл:
sourcearray - одномерный массив, элементы которого являются строками. Он может быть получен, например, как результат расщепления строки в массив.match - образец поиска. Строка, вхождение которой ищется в каждом элементе исходного массива.include - необязательный аргумент булевого типа. По умолчанию имеет значение True, означающее, что элементы, удовлетворяющие образцу, входят в результирующий массив. Если задано значение False, то результирующий массив составляется из элементов, не удовлетворяющих образцу.compare - имеет обычный смысл.В качестве результата возвращается массив отфильтрованных элементов.
Наш пример будет представлять расширенный вариант уже приводившейся процедуры SplitAndJoin:
Public Sub SplitAndJoin()
'В этой процедуре сложное предложение разделяется на простые
'А затем после обработки строка восстанавливается
'Здесь же демонстрируется фильтрация элементов массива
'Объявляем динамический массив
Dim Simple() As String, i As Byte
Dim Simple1() As String, Res As String
Dim Simple2() As String
'Размерность массива Simple устанавливается автоматически
'в соответствии с размерностью массива, возвращаемого функцией Split
Simple = Split("А это пшеница, которая в темном чулане хранится в доме, " _
"который построил Джек", ", ")
'Создаем новый массив
ReDim Simple1(1 To UBound(Simple) + 2)
Simple1(1) = "А это веселая птица - синица"
Simple1(2) = "которая часто ворует пшеницу"
For i = 3 To UBound(Simple1)
Simple1(i) = Simple(i - 2)
Next i
'Создаем строку из массива Simple1
Res = Join(Simple1, ", ")
Debug.Print Res
'Фильтрация элементов массива
Simple2 = Filter(Simple1, "котор")
Res = Join(Simple2, ", ")
Debug.Print Res
Simple2 = Filter(Simple1, "котор", False)
Res = Join(Simple2, ", ")
Debug.Print Res
End Sub
Вот результаты отладочной печати:
А это веселая птица - синица, которая часто ворует пшеницу, которая в темном чулане хранится в доме, который построил Джек которая часто ворует пшеницу, которая в темном чулане хранится в доме, который построил Джек А это веселая птица - синица
Какие бы новые возможности не появлялись в системе, программист всегда будет создавать свой собственный инструментарий, расширяющий стандартные возможности. В предыдущих разделах мы уже приводили разработанные нами функции MyReplace, DelStr, являющиеся частью такого инструментария. Сейчас мы хотим привести еще несколько функций из нашего инструментального набора. Их приведение полезно и по той причине, что эти функции используют новые средства разбора строк. Мы приведем вариации стандартных функций Replace, Filter и Split.
Несмотря на то, что уже один вариант функции Replace приведен, мы разработали еще два варианта. Оба они отражают часто возникающую необходимость производить замену, основанную не на точном совпадении, а на совпадении с шаблоном, как это делается в операции Like.Наши новые функции позволяют разные подстроки заменять одним и тем же новым содержимым. Нам не удалось написать универсальную и эффективную реализацию такой функции, поэтому мы ограничились двумя частными, но важными для практики случаями.
Важным частным случаем, когда легко реализовать замену, основанную на шаблоне, представляют строки, допускающие разбор их на элементы. В этом случае алгоритм замены понятен, - строка разбирается на элементы, используя новые возможности, предоставляемые функцией Split ; затем к каждому элементу применяется операция Like для сравнения с шаблоном; на последнем шаге применяется Join для восстановления строки. Приведем текст нашей функции:
Public Function WildReplace(ByVal expr As String, ByVal find As String, _ ByVal Rep As String, Optional ByVal delimiter As String = " ") As String 'Эта функция применима в том частном случае, когда строка - источник, заданная аргументом Expr 'представляет совокупность элементов, отделяемых разделителями. 'Типичный пример - строка представляет совокупность слов, разделенных пробелами. 'Как и обычная функция Replace эта функция производит замену вхождений элемента (подстроки) новым значением 'Отличие от стандартной функции состоит в том, что заменяемый элемент (подстрока) find 'задается шаблоном. В результате разные элементы могут быть заменены на новое значение. 'Для этого частного случая WildReplace существенно расширяет стандартные возможности Replace 'Алгоритм основан на том факте, что строка допускает разбор ее на элементы, 'а к элементам применима операция Like - сравнения с шаблоном. Dim Words() As String, i As Long, Res As String 'Разбор строки на элементы Words = Split(expr, delimiter) 'Сравнение элементов с шаблоном и замена в случае совпадения For i = LBound(Words) To UBound(Words) If Words(i) Like find Then Words(i) = Rep Next i 'Сборка строки Res = Join(Words, delimiter) WildReplace = Res End Function
Параметры этой функции имеют тот же смысл, что и для стандартной функции Replace. Существенное отличие состоит в том, что специальные символы шаблона допустимы в строке поиска find. Приведем теперь WildReplace:
Public Sub testWildReplace() Dim Inf As String, SecretInf As String Inf = "Agent001, Agent007, Агент Майор Пронин, Agent008" SecretInf = WildReplace(Inf, "Agent###", "Agent***", ", ") Debug.Print SecretInf End Sub
Вот результаты ее работы:
Agent***, Agent***, Агент Майор Пронин, Agent***
Другим важным случаем, допускающим простую реализацию, является случай, когда замене подлежат вхождения отдельного символа, а не целой подстроки, как в общем случае. Наша функция производит замену разных символов из заданного множества на один и тот же символ. Алгоритм основан на обычном линейном просмотре и посимвольном сравнении. Сама замена производится путем выделения и конкатенации.
Эта функция понадобилась нам для реализации нового варианта функции расщепления строки, хотя она полезна и сама по себе. Приведем ее текст:
Public Function CharSetReplace(ByVal expr As String, ByVal find As String, _ ByVal Rep As String) 'Эта функция применима в том частном случае, когда в строке - источнике Expr 'требуется произвести замену символов, входящих в множество, заданное аргументом find 'на символ (последовательность символов), заданных аргументом rep Dim Source As String Dim i As Long, Sym As String Dim N As Long N = Len(expr) Source = expr For i = 1 To N Sym = VBA.Mid$(Source, i, 1) If InStr(1, find, Sym) Then Source = VBA.Left$(Source, i - 1) Rep VBA.Mid$(Source, i + 1) End If Next i CharSetReplace = Source End Function
Приведем теперь CharSetReplace:
Public Sub testCharSetReplace() Dim Text As String Text = "test123" Text = CharSetReplace(Text, "1234567890", "*") Debug.Print Text Text = "Шла кошка по крыше" Text = CharSetReplace(Text, "аоеиуыяюэ", "*") Debug.Print Text End Sub
В результате ее работы в окне отладки появляются следующие данные:
Шл* к*шк* п* кр*ш* Agent***, Agent***, Агент Майор Пронин, Agent***
Во многих ситуациях есть необходимость производить фильтрацию элементов массива, основываясь на шаблоне, которому должны удовлетворять фильтруемые элементы. Здесь есть полная аналогия с тем, что есть потребность производить замену на основе шаблона, а не точного образца. Поэтому естественно наше желание расширить стандартные возможности функции Filter, тем более, что реализуется эта возможность достаточно просто. Рассмотрим реализацию нашей функции:
Public Function WildFilter(sourcearray() As String, ByVal match As String, _ Optional ByVal include As Boolean = True) As Variant 'Эта функция фильтрует элементы исходного массива, проверяя их на соответствие шаблону match 'В отличие от стандартной функции Filter элементы проверяются на точное соответствие 'но шаблон match может включать специальные символы шаблона. 'Возвращается массив отобранных элементов. Если булев параметр include равен True 'то отбираются элементы, совпадающие с шаблоном, в противном случае - не совпадающие. Dim Sel() As String Dim i As Long, j As Long j = 1 If include Then For i = LBound(sourcearray) To UBound(sourcearray) If sourcearray(i) Like match Then ReDim Preserve Sel(1 To j) Sel(j) = sourcearray(i): j = j + 1 End If Next i Else For i = LBound(sourcearray) To UBound(sourcearray) If Not (sourcearray(i) Like match) Then Sel(j) = sourcearray(i): j = j + 1 End If Next i End If WildFilter = Sel End Function
Параметры этой функции сохраняют смысл стандартной функции Filter. Единственное отличие состоит в том, что теперь аргумент match задает настоящий шаблон, допускающий специальные символы. Чтобы быть справедливым, заметим, что теперь речь идет о точном соответствии шаблону, в то время, как в стандартной функции речь идет о вхождении шаблона в элемент. Так что обе функции имеют свои сферы применения. Реализация достаточно очевидна и основана на прямом использовании операции Like. Приведем тестовый пример, в котором вызывается наша функция:
Public Sub testWildFilter() Dim Txt As String, resTxt As String Dim Artxt() As String, ResAr() As String Txt = "123, 5, 117, 7, 189" Artxt = Split(Txt, ", ") ResAr = WildFilter(Artxt, "1##") resTxt = Join(ResAr, ", ") Debug.Print resTxt ResAr = WildFilter(Artxt, "[3-57-9]") resTxt = Join(ResAr, ", ") Debug.Print resTxt End Sub
Приведем результаты ее работы:
123, 117, 189 5, 7
Завершим обзор рассмотрения собственных инструментальных средств описанием функции WildSplit. Когда мы описывали стандартную функцию разбора строки на элементы, мы отмечали, что она, к сожалению, не применима в тех достаточно часто встречающихся ситуациях, когда элементы разделяются разными разделителями. Наша функция в какой то мере пытается восполнить существующий пробел. Она разделяет строку на элементы, которые могут в строке разделяться разными символами, представляющими разделители элементов, возможно, окруженные пробелами.
Алгоритм реализации прост. Он использует ранее написанную функцию замены CharSetReplace, заменяя все разделители одним. После этого становится возможным применить стандартную функцию разбора. На заключительном этапе у элементов убираются пробелы слева и справа. Следует отметить и недостаток этой функции, - преобразование необратимо и последующее применение Join не позволяет восстановить исходную строку в первозданном виде. Приведем текст этой функции:
Public Function WildSplit(expr As String, Optional ByVal delimiter As String = " ", _ Optional ByVal limit As Long = -1, Optional ByVal compare As VbCompareMethod = vbBinaryCompare) As Variant 'Также, как и стандартная функция Split, эта функция расщепляет строку - источник expr 'на элементы, используя разделители, заданные аргументом delimiter 'Отличие состоит в том, что при выделении элементов предполагается, что разделителем 'может быть любой из символов множества delimeter, возможно, окруженный пробелами Dim Source As String, ResAr() As String Dim find As String, Rep As String Dim i As Long Source = expr find = VBA.Mid$(delimiter, 2) Rep = VBA.Left$(delimiter, 1) 'Заменяем в строке все разделители на один из них Source = CharSetReplace(Source, find, Rep) 'Теперь используем стандартную функцию Split ResAr = Split(Source, Rep, limit, compare) 'Удаляем пробелы For i = LBound(ResAr) To UBound(ResAr) ResAr(i) = VBA.Trim$(ResAr(i)) Next i WildSplit = ResAr End Function
Приведем также WildSplit:
Public Sub testWildSplit() Dim Txt As String, Res As String Dim Items() As String Txt = "a * b - (c+d)/v" Items = WildSplit(Txt, "+-*/") Res = Join(Items) Debug.Print Res End Sub
Вот результаты ее работы:
a b (c d) v
На этом мы закончим наш затянувшийся разговор о работе со строковыми данными.
Для того, чтобы обеспечить программисту возможность корректно работать с датами и временем, VBA предоставляет Date, хранящий дату и время. Над данными этого типа можно выполнять некоторые операции, но, конечно же, при работе с ними чаще всего используются специальные встроенные функции. Попытаемся коротко рассмотреть основные возможности работы с датами. Прежде всего, заметим, что возможный диапазон дат охватывает даты от 1.1.100 года до 1-го января 9999 года. Если говорить о внутреннем представлении дат, занимающих 4 байта памяти, то целая часть хранит число дней от некоторой начальной даты, дробная часть хранит время от полуночи. Начальной датой является 30-е декабря 1899 года. Благодаря такому внутреннему представлению сложение и вычитание целого числа воспринимается как прибавление или вычитание дней.
При присваивании значений переменным # " или задавать ее как строковую константу. При задании даты в ограничителях, например #9, May, 99 # она автоматически преобразуется к стандартному формату #5/9/99# (месяц/день/год). Вот пример некоторых действий над датами:
Public Sub WorkWithDates() 'Работа с датами Dim dat1 As Date, dat2 As Date, dat3 As Date 'Присваивание дат dat1 = 12 dat2 = 9 / 5 / 99 dat3 = #9/5/1999# Debug.Print dat1, dat2, dat3 dat1 = "15/7/99" dat2 = #5/9/1999# dat3 = dat3 + 100 Debug.Print dat1, dat2, dat3 If dat3 > dat2 Then Debug.Print dat3 - dat2 Else Debug.Print dat2 - dat3 End If End Sub
Приведем результаты выполнения этой программы:
11.01.1900 0:26:11 05.09.99 15.07.99 09.05.99 14.12.99 219
Прокомментируем результаты, поскольку некоторые из них могут вызывать недоумение. Остановимся на первых двух присваиваниях. Чтобы понять их, нужно вспомнить внутренне представление дат. В первом случае к начальной дате прибавляется 12 дней, отсюда и получается 11 января 1900 года. Во втором случае, поскольку выражение в правой части не заключено в ограничители, оно вычисляется как обычное арифметическое выражение, полученное дробное число воспринимается как время от начала суток, - оно и печатается. Обратите внимание, на третье присваивание, первое число воспринимается как номер месяца, для российских программистов здесь кроется источник возможных ошибок, поэтому лучше задавать даты, используя названия месяцев, которые автоматически будут преобразованы в соответствующий формат.
Далее в программе продемонстрированы некоторые возможности выполнения операций над датами - прибавление целого числа дней, сравнение дат, вычитание дат. Заметьте, вычитание дат дает разницу между ними, выраженную в днях.
Как мы уже говорили, основная работа с датами выполняется с использованием встроенных функций. Рассмотрим коротко их описание и назначение.
Date - возвращает текущую дату.Time - возвращает текущее время по часам компьютера.Now - возвращает значение типа Variant (Date), содержащее текущую дату и время по системному календарю и часам компьютера.Функция DateAdd предназначена для добавления или вычитания указанного временного интервала из значения даты. Заметьте, с ее помощью можно задавать временной интервал не только в днях, но и в месяцах, годах и так далее.
DateAdd(interval, number, date)
Аргумент interval - строка, указывающая тип добавляемого временного интервала, number - число временных интервалов, на которое следует изменить дату, date - дата, к которой добавляется указанный временной интервал. Допустимые значения аргумента interval: приведены в следующей таблице.
| Значение | Описание |
|---|---|
| yyyy | Год. |
| Q | Квартал. |
| m | Месяц. |
| Y | День года. |
| D | День месяца. |
| w | День недели. |
| ww | Неделя. |
| H | Часы. |
| N | Минуты. |
| S | Секунды. |
Для примера, приведем два вызова этой функции в окне отладки:
? DateAdd("m", 1, "31-янв-95")
28.02.95
? DateAdd("m", -1, "31-янв-95")
31.12.94
Функция DateDiff предназначена для определения времени, прошедшего между двумя датами. Например, с помощью этой функции можно вычислить число дней между двумя датами или число недель между текущей датой и концом года. Синтаксис:
DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]])
interval задает тип временного интервала при вычислении разности между датами date1 и date2, - его возможные значения те же, что и для функции DateAdd,date1 и date2 - две даты, разность между которыми следует вычислить.firstdayofweek - константа, указывающая первый день недели (по умолчанию считается, что неделя начинается с воскресенья).firstweekofyear - константа, указывающая первую неделю года (по умолчанию первой неделей считается неделя, содержащая 1 января).Приведем примеры вызова этой функции в окне отладки:
? DateDiff("m", "18.10.55", "31-янв-95")
471
? DateDiff("Y", "18.11.97", "01.01.97")
-321
Функция DatePart предназначена для определения указанного компонента даты. Например, с помощью этой функции можно определить день недели или текущий час. Синтаксис:
DatePart(interval, date[,firstdayofweek[, firstweekofyear]])
interval задает тип возвращаемого временного интервала,date - дата, подлежащая обработке.Firstdayofweek и Firstweekofyear имеют тот же смысл, что и для функции DateDiff.Примеры вызова:
? DatePart("ww", "02.09.89")
35
? DatePart("w", "02.09.89")
7
Функция DateSerial позволяет вычислить значение даты типа Variant (Date) по ее компонентам, - году, месяцу и дню. Ее синтаксис:
DateSerial(year, month, day)
Значение каждого аргумента должно лежать в соответствующем диапазоне: 100 -- 9999 для года, 1 - 31 для дней и 1 - 12 для месяцев. Можно также использовать для аргументов числовые выражения для описания относительной даты.
Примеры:
? DateSerial(1997 - 25, 10 - 2, 18 - 1) 17.08.72 ? DateSerial(Year(Now) - 3,Month(Now) - 2, Day(Now) - 1) 08.03.96
Функция DateValue переводит аргумент-строку в дату. В отличие от функции преобразования CDate, функция DateValue правильно обрабатывает допустимые даты, содержащие полные или краткие названия месяцев.
Ее синтаксис:
DateValue(date)
Аргумент date может задавать как дату, так и время. Возможные разные форматы задания даты, в том числе и с названиями месяцев:
Примеры:
? DateValue ("25.04.1997")
25.04.97
? DateValue ("04.25.1997")
25.04.97
? DateValue ("25 апреля 1997")
25.04.97
? DateValue ("25 - апр.-97")
25.04.97
? DateValue ("Апрель, 25, 97")
25.04.97
? DateValue ("25/04/1997")
25.04.97
? DateValue ("25.04")
25.04.99
Последнее равенство связано с тем: что при отсутствии года, функция DateValue использует текущий год по системному календарю компьютера.
Функции Day(date), Month(date), и Year(date) являются, в некотором смысле, обратными к двум предыдущим. Они по аргументу-дате определяют номер дня, месяца и года. Функция Weekday(date, [firstdayofweek]) возвращает значение типа Variant (Integer), содержащее целое число, представляющее день недели. Второй аргумент задает первый день недели (по умолчанию - воскресенье ). Значение 2 соответствует понедельнику.
Функции Hour(время), Minute(время) и Second(время) по аргументу, являющемуся числовым или строковым выражением, представляющим время, возвращает содержащее целое число, которое представляет, соответственно, часы, минуты и секунды в значении времени.
Функция TimeSerial(hour, minute, second) вычисляет результат Variant (Date), содержащее значение времени, соответствующее указанным часу, минуте и секунде.
Функция TimeValue(время) возвращает значение типа Variant (Date), содержащее время. Аргумент время обычно задается строковым выражением, представляющим время от 0:00:00 (12:00:00 A.M.) до 23:59:59 (11:59:59 P.M.) включительно. Задавать его нужно корректно, чтобы не возникало ошибок, как в последних двух примерах:
? Year(Now)
1999
? Month(now)
5
? Day(Now)
9
? Hour(Now)
11
? Minute (Now)
57
? Second(Now)
28
? TimeSerial(Hour(Now), Minute(now), Second(Now))
11:59:00
? TimeValue("12:30 PM")
12:30:00
? TimeValue("12.30")
0:00:00
? TimeValue("12,5")
0:00:00
Функция Timer возвращает значение типа Single, представляющее число секунд, прошедших после полуночи. Эта функция широко используется при проведении хронометража вычислений в программах. Чтобы повысить эффективность выполнения VBA программ зачастую приходится прибегать к проведению хронометража, чтобы выявить наиболее критичные по времени выполнения участки программ, а затем уже применять специальные меры для ускорения вычислений. Приведем сейчас пример, в котором анализируется время, затрачиваемое для выполнения операций над арифметическими данными разных подтипов.
Public Sub Speed() 'Эта программа выполняет замеры времени вычислений над данными разного типа Dim Start As Single, Finish As Single Dim i As Long, j As Long Dim bx As Byte, by As Byte, bz As Byte Dim ix As Integer, iy As Integer, iz As Integer Dim lx As Long, ly As Long, lz As Long Dim sx As Single, sy As Single, sz As Single Dim dx As Double, dy As Double, dz As Double For i = 1 To 5 'Внешний цикл для повторения замеров времени Start = Timer For j = 1 To 100000 bx = 99: by = 101 bz = by * (by - bx) \ by Next j Finish = Timer Debug.Print "Тип Byte: Время ", i, " = ", Finish - Start Debug.Print "bz = ", bz Start = Timer For j = 1 To 100000 ix = 99: iy = 101 iz = iy * (iy - ix) \ iy Next j Finish = Timer Debug.Print "Тип Integer: Время ", i, " = ", Finish - Start Debug.Print "iz = ", iz Start = Timer For j = 1 To 100000 lx = 99: ly = 101 lz = ly * (ly - lx) \ ly Next j Finish = Timer Debug.Print "Тип Long: Время ", i, " = ", Finish - Start Debug.Print "lz = ", lz Start = Timer For j = 1 To 100000 sx = 99: sy = 101 sz = sy * (sy - sx) / sy Next j Finish = Timer Debug.Print "Тип Single: Время ", i, " = ", Finish - Start Debug.Print "sz = ", sz Start = Timer For j = 1 To 100000 dx = 99: dy = 101 dz = dy * (dy - dx) / dy Next j Finish = Timer Debug.Print "Тип Double: Время ", i, " = ", Finish - Start Debug.Print "dz = ", dz Next i End Sub
Представим результаты вычислений в виде таблицы. Они представляют интерес сами по себе. Заметьте, что наибольшее время потребовалось для вычислений над самым коротким типом - Byte, а в целом вычисления над разными типами выполняются примерно с одинаковой скоростью.
| Тип / Номер Эксперимента | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
Byte |
0.3515625 | 0.359375 | 0.3320313 | 0.3320313 | 0.34375 |
Integer |
0.2421875 | 0.2382813 | 0.2421875 | 0.2421875 | 0.2382813 |
Long |
0.2890625 | 0.28125 | 0.28125 | 0.28125 | 0.28125 |
Single |
0.2929688 | 0.2695313 | 0.28125 | 0.28125 | 0.28125 |
Double |
0.3085938 | 0.2890625 | 0.2929688 | 0.2929688 | 0.28125 |
Как мы уже говорили, встроенных функций множество. Помнить их всех не требуется, поскольку под рукой всегда есть справочная система. Для общего знакомства рассмотрим еще некоторые группы функций.
К этой группе относится функция TypeName, которая по имени переменной возвращает значение типа String, представляющее ее тип.
Вызов имеет вид:
TypeName(имяПеременной)
где параметр имяПеременной представляет выражение типа Variant, определяющее любую переменную, за исключением переменной с определяемым пользователем типом.
Строка, возвращаемая функцией TypeName, может быть любой из следующих: Byte, Integer, Long, Single, Double, Currency, Decimal, Date, String, Boolean, Error, Empty (если переменная не инициализирована), Null, Object, Unknown (тип неизвестен), Nothing (объектная переменная, не содержащая ссылки на объект).
Для проверки типа имеется также набор функций с булевыми значениями. Они применяются к переменным или выражениям (часто имеющим тип Variant ) и определяют, имеют ли те заданный тип или значение. В следующей таблице перечислены функции этой группы и указаны определяемые ими типы.
| Функция | Что проверяет |
|---|---|
IsArray(переменная) |
Является ли переменная массивом. |
IsDate(выражение) |
Может ли значение выражения быть преобразовано в значение даты. |
|
Была ли инициализирована переменная. |
IsError(выражение) |
Представляет ли выражение значение ошибки. |
IsNull(выражение) |
Является ли результатом выражения пустое значение ( Null ). |
IsNumeric(выражение) |
Имеет ли выражение числовое значение. (Для выражений False ). |
IsObject(переменная) |
Представляет ли переменная объектную переменную. Возвращает True, даже для переменной со значением Nothing. |
Для приведения данных к нужному типу в VBA включен обширный набор функций: CBool, CByte, CCur, CDate, CDbl, CDec, CInt, CLng, CSng, CStr, CVar, CVErr, Fix, Int.
Все они имеют следующий синтаксис:
ИмяФункции(выражение)
Обязательный аргумент выражение является любым строковым выражением или числовым выражением. В следующей таблице указаны типы возвращаемых значений и их диапазоны.
| Функция | Тип | Диапазон аргумента выражение |
|---|---|---|
CBool |
Boolean |
Любая допустимая строка или числовое выражение. Например, Cbool(0)= Cbool("00")= False, Cbool(1)=Cbool("-6")=True. |
CByte |
Byte |
От 0 до 255. |
CCur |
Currency |
От -922 337 203 685 477,5808 до 922 337 203 685 477,5807.Например, CCur("25,878755")= 25.8788, а CCur("25.878755") - ошибочный вызов, т. к. в строке отделять дробную часть должна запятая. |
CDate |
Date |
Любое допустимое выражение даты. Например, Cdate(32444.5)=28.10.88 12:00:00, Cdate("17/08/1972")=17.08.72, Cdate("6:45:28 PM")= 18:45:28. |
CDbl |
Double |
-4,94065645841247E-324 для отрицательных чисел; от 4,94065645841247E-324 до 1,79769313486232E308 для положительных чисел. |
CDec |
Decimal |
+/-7,9228162514264337593543950335. Минимальное ненулевое число 0,0000000000000000000000000001. |
CInt |
Integer |
От -32 768 до 32 767 с округлением дробной части. |
CLng |
Long |
От -2 147 483 648 до 2 147 483 647 с округлением дробной части. |
CSng |
Single |
От -3,402823E38 до -1,401298E-45 для отрицательных чисел; от 1,401298E-45 до 3,402823E38 для положительных чисел. |
CVar |
Variant |
Диапазон значений Double для числовых значений. Диапазон значений String для нечисловых значений. |
CStr |
String |
Возвращаемые значения функции CStr зависят от аргумента выражение. Например, CStr(True) = "Истина", CStr(122.344) = "122.344", CStr(#10/24/47#) = 24.10.47 |
Если переданное в функцию значение аргумента выражение находится вне допустимого диапазона для соответствующего типа данных, возникает ошибка.
Функция CVErr возвращает значение типа Variant с подтипом Error, содержащее код ошибки, указанный пользователем. Она используется для создания определяемых пользователем ошибок.
Если дробная часть числа равна 0,5, то функции CInt и CLng всегда округляют число до ближайшего четного числа. Например, Cint(0,5)= 0, а Cint(1,5)= 2.
Функции Fix и Int вычисляют целую часть числа (без округления). Они отличаются на отрицательных числах: Fix (-7.6) = -7, а Int(-7.6) = -8.
Числовые и строковые данные, данные
Функция возвращает строку, отформатированную в соответствии с указаниями, заданными при вызове. Ее синтаксис:
Format(expression[, format[, firstdayofweek[, firstweekofyear]]])
Ее параметры:
expression - любое правильное выражение.Format - имя встроенного параметра или определение пользовательского формата. Если параметр опущен, то применяется формат, зависящий от типа первого аргументаПриведем ряд примеров, в которых используется форматирование чисел на основе форматов, определяемых по умолчанию и пользователем:
? VBA.Format(55) 55 ? VBA.Format(5.5) 5,5 ? VBA.Format(-52.125, "##0.#0") -52,13 ? VBA.Format(-52.125, "000.##0") -052,125 ? VBA.Format(1152.125, "#,##0.#0") 1 152,13 ? VBA.Format(0.52125, "0.##0%") 52,125%
Несколько примеров форматирования строк:
? VBA.Format("5.4")
05.04.99
? VBA.Format("5,4")
5,4
? VBA.Format("Мария", ">")
МАРИЯ
? VBA.Format("Мария", "<")
мария
? VBA.Format("Мария", "> Это ")
Это МАРИЯ
Даты форматируются обычно с использованием встроенных форматов, но можно применять и собственные определения форматов. Вот несколько примеров:
? VBA.Format(VBA.Time, "Long Time") 16:24:57 ? VBA.Format(VBA.Time, "Short Time") 16:25 ? VBA.Format(VBA.Time, "hh/mm/ss") 16.26.03 ? VBA.Format(VBA.Date, "Long Date") 9 Май 1999 г. ? VBA.Format(VBA.Date, "Short Date") 09.05.99 ? VBA.Format(VBA.Date, "yy/mm/dd") 99.05.09
Функция Format является общей функцией применимой к произвольным выражениям. Остальные функции применимы к выражениям специального типа, предоставляя некоторые дополнительные возможности. Мы не станем их подробно рассматривать, ограничившись простым перечислением:
FormatCurrency - возвращает денежное выражение, используя денежный знак.FormatDataTime - возвращает дату или время.FormatNumber - возвращает выражение, отформатированное как число.FormatPercent - возвращает выражение, заданное в процентах, с указанием знака процента.Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.