Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2 Курс Информатика VBA(ЗО) / Книги / В.Д.Хорев - Самоучитель программирования на VBA в Microsoft Office

.pdf
Скачиваний:
2700
Добавлен:
31.05.2015
Размер:
21.66 Mб
Скачать

Сумма прописью 103

2.Если одновременно открыто несколько приложений Office или, по крайней мере, несколько рабочих книг Excel, то необходимо выделить имя требуемой книги в окне VBA-проекта (см. рис. ниже). Если открыта только одна рабочая книга, то ничего выделять не требуется.

3.В строке меню редактора Visual Basic выберите команду Insert | Module (Вставка | Модуль).

Врезультате в состав рабочей книги будет включен программный модуль Module1 (“Модуль1”, см. рисунок), а в центре экрана появится окно программного кода вновь созданного модуля, пока что пустое.

Функции

Второе новое понятие, с которым нам необходимо познакомиться, — это процедура-функция.

ПРИМЕЧАНИЕ

Справка MS Office трактует и Sub, и Function, как понятия процедуры, но двух разных типов.

Все макросы VBA, которые до сих пор были рассмотрены, относились к типу процедурыподпрограммы (Sub). Но существуют также процедуры типа Function: такие процедуры возвращают значение подобно встроенным функциям Visual Basic. Например, использованная ранее функция Str() возвращает строковое значение взамен переданного ей в параметре числа.

Постановка задачи

Создадим функцию, Сумма_прописью(), которая будет возвращать строку суммы прописью, соответствующую переданному ей числовому значению в денежном формате. Тогда ею сможет воспользоваться любой из макросов рабочей книги. А самое главное — несколько макросов смогут использовать ее одновременно и независимо друг от друга.

Итак, перед нами пустое окно кода только что созданного программного модуля, в котором предстоит создать процедуру-функцию.

Как добавить в модуль процедуру-функцию

1.Выберите в строке меню редактора Visual Basic команду

Insert | Procedure (Вставка | Процедура). В результате откроется диалоговое окно Add Procedure (Вставка процедуры).

2.Установите переключатель Type (Тип) в положение

Function.

3.Установите переключатель Scope (Область определения) в положение Public (Общая).

4.Введите в поле Name (Имя) имя функции (в данном случае это должна быть строка “Сумма_прописью”).

5.Закройте диалоговое окно щелчком на кнопке OK. В результате в окне кода будет вставлена процедурафункция с заданными параметрами.

Полученную “заготовку” процедуры необходимо отредактировать в соответствии с возлагаемыми на нее задачами. В автоматически созданной заготовке, конечно, не указан тип возвращаемого значения и параметр.

Параметр N представляет значение, передаваемое в функцию извне, это значение должно относиться к “денежному” числовому типу Currency. Сама же функция должна иметь тип String, поскольку возвращает строковое значение. В результате окно кода модуля должно приобрести вид, как на рис. 4.1.

104 Глава 4. Практикум программирования на VBA для Excel и Word

Рис. 4.1. Окно кода программного модуля — автоматически созданная процедура откорректирована вручную

см. также в приложении раздел “Currency”.

Разрабатываем исходный текст функции Сумма_прописью()

Остается создать собственно сам программный код функции. Код такого назначения много раз реализовывался на различных языках программирования, при этом использовались различные алгоритмы, использующие в каждом случае особенности конкретного языка. Не будем “мудрствовать лукаво”, во-первых, потому, что возможности Visual Basic сравнительно невелики, и, вовторых, чтобы не усложнять программный код в синтаксическом отношении. В результате исходный текст функции получится несколько громоздким (в принципе, даже на VBA его можно было бы сделать более компактным), но при этом достаточно компактным для понимания.

Итак, представим, что какой-то макрос обратился к функции Сумма_прописью, и начинается ее выполнение. Переменная N в этот момент содержит числовое “денежное” значение, переданное в функцию извне. Чтобы как-то работать с ним, нам необходимо преобразовать его в строку символов. Объявим для этого строковую переменную S, а вместе с ней — переменную SUM, в которой затем будем “строить” результирующую строку суммы прописью:

Dim S, SUM As String

Использование функции FORMAT () для преобразования числа в строку

Чтобы преобразовать число в строку, воспользуемся функцией Format:

S = Format(N, "000000.00")

В результате выполнения этого оператора функция Format вернет в переменную S строковое значение, представляющее собой денежную сумму цифрами в заданном формате. То есть, для N = 123456.78 вернется строка “123456.78”, а для N = 123 вернется “000123.00”.

см. также в приложении раздел “Функция Format”.

Теперь можно просматривать последовательно первые шесть символов строки S, и, в соответствии с содержащимися в этих позициях цифрами, создавать строку суммы прописью.

ПРИМЕЧАНИЕ

В данном примере задача решается для шестизначных сумм, то есть сумм, состоящих из не более, чем шести знаков перед запятой. Можно без труда распространить такое решение на большее число знаков. Здесь задача ограничивается сознательно, поскольку избранный способ решения делает код (в случае большого числа знаков) слишком громоздким. Это не составляет проблемы в окне редактора Visual Basic, поскольку код процедуры состоит из повторяющихся похожих структур, которые можно быстро создать при помощи операций копирования и вставки. Однако, заниматься подобными построениями на книжной странице вряд ли целесообразно.

Сумма прописью 105

Анализ текстовой строки

Проанализируем вначале первый символ строки. Для этого воспользуемся строковой функцией Mid. Эта функция возвращает заданное число символов (третий параметр) из заданной позиции (второй параметр) в заданной строке (первый параметр). Например, если переменная S содержит строку “123456.78”, то выражение

Mid(S, 3, 1)

вернет символ “3” (если бы третий параметр был равен, скажем, 5, то вернулась бы строка

“345.7”).

см. также в приложении раздел “Выделение подстроки”.

Итак, можно получить первый символ строки S при помощи выражения Mid(S, 1, 1). Проанализируем этот символ, представляющий шестой знак суммы, то есть сотни тысяч:

Select Case Mid(S, 1, 1)

Case "0":

Case "1": SUM = SUM + "сто "

Case "2": SUM = SUM + "двести "

Case "3": SUM = SUM + "триста "

Case "4": SUM = SUM + "четыреста "

Case "5": SUM = SUM + "пятьсот "

Case "6": SUM = SUM + "шестьсот "

Case "7": SUM = SUM + "семьсот "

Case "8": SUM = SUM + "восемьсот "

Case "9": SUM = SUM + "девятьсот "

End Select

Если в числе нет соответствующего разряда, то в этой позиции окажется символ “0” и со строкой SUM ничего не произойдет. В противном случае в нее будет помещено одно из слов, обозначающих сотни тысяч.

Задача была бы совсем тривиальной, если бы могли действовать далее аналогичным образом, разбирая строку разряд за разрядом и просто подставляя необходимые слова. Увы, русские имена числительные не столь единообразны. Для чисел от 11 до 20 действуют совершенно иные правила, чем, скажем, для чисел от 21 до 30, и поэтому разряды, относящиеся к десяткам и единицам тысяч, придется анализировать “в комплексе” друг с другом. Надо предусмотреть три принципиально разных варианта. Для чисел от 10 до 19 необходимо использовать отдельную конструкцию, которая “покрывала” бы сразу два разряда — десятки и единицы. Чтобы выделить этот случай, необходимо сравнить второй разряд с единицей:

If Mid(S, 2, 1) = "1" Then

...

End If

При этом, если второй разряд действительно оказался равным единице, анализировать далее следует третий разряд:

Select Case Mid(S, 3, 1)

Case "0": SUM = SUM + "десять " Case "1": SUM = SUM + "одиннадцать "

...

Case "9": SUM = SUM + "девятнадцать " End Select

106 Глава 4. Практикум программирования на VBA для Excel и Word

Для всех остальных случаев второй и третий разряды необходимо формировать в строке суммы раздельно, например, “двадцать” + “одна” (не забываем, речь идет о тысячах и поэтому “од- на”). Следующая конструкция отфильтрует все десятки и единицы кроме диапазона 10…19:

If Mid(S, 2, 1) > "1" _ Or _

Mid(S, 2, 1) = "0" Then

...

End If

Внутри нее можно добавить к строке суммы прописью десятки и единицы обычным образом. Вначале:

Select Case Mid(S, 2, 1)

Case "2": SUM = SUM + "двадцать "

...

Case "9": SUM = SUM + "девяносто " End Select

А затем:

Select Case Mid(S, 3, 1)

Case "1": SUM = SUM + "одна "

...

Case "9": SUM = SUM + "девять " End Select

Далее, перед переходом от тысяч к “простым” сотням, десяткам и единицам, нас подстерегает маленькая проблема. Нельзя просто добавить к строке SUM слово “тысяч” и двигаться дальше. Возможны случаи как “тысяч” (например, 11, 100, 155), так и “тысяча” (например, 1, 21, 101 ) и даже “тысячи” (например, 2, 34, 503). Все эти варианты необходимо правильно распознать и добавить к строке SUM верное слово. Начнем с того, что тысяч в числе суммы вообще может не быть и в этом случае соответствующее слово добавлять нельзя:

If SUM <> "" Then

...

If

Затем, разделим варианты на “от 10 до 19” и “все остальное”:

If Mid(S, 2, 1) = "1" Then SUM = SUM + "тысяч "

Else

...

End If

Для “всего остального” будем различать три случая — одна тысяча, две/три/четыре тысячи и опять же “все остальное”:

Select Case Mid(S, 3, 1)

Case "1": SUM = SUM + "тысяча "

Case "2", "3", "4": SUM = SUM + "тысячи "

Case Else: SUM = SUM + "тысяч "

End Select

Сумма прописью 107

Строка Case Else выполняется в случае, когда ни одна из строк Case не выполнена. В результате получим весьма разветвленную конструкцию:

If SUM <> "" Then

If Mid(S, 2, 1) = "1" Then SUM = SUM + "тысяч "

Else

Select Case Mid(S, 3, 1)

Case "1": SUM = SUM + "тысяча "

Case "2", "3", "4": SUM = SUM + "тысячи " Case Else: SUM = SUM + "тысяч "

End Select

End If

End If

см. также в приложении раздел “Конструкция Select Case … End Select”.

Следующую тройку разрядов 4-й, 5-й и 6-й, то есть сотни, десятки и единицы, можно обработать почти точно таким же способом. Единственная особенность, из-за которой их приходится обрабатывать отдельно, заключается в разном роде слов “тысяча” и “рубль”. Придется использовать “один” вместо “одна” и “два” вместо “две”.

К полученной в результате строке SUM необходимо еще добавить окончание “руб.” и число копеек:

SUM = SUM + " руб. " + Mid(S, 8, 2) + " коп."

Функция Mid извлекает два последних символа из строки S, в предположении, что это должны быть цифры, соответствующие числу копеек в сумме (по бухгалтерским правилам, в сумме прописью число копеек пишется цифрами). О том, чтобы это действительно было число копеек, “позаботились” функция Format и денежный числовой тип Currency.

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

Сумма_прописью = UCase(Left(SUM, 1)) + Right(SUM, Len(SUM) - 1)

Функция UCase уже была рассмотрена (см. в гл. 3 раздел “Анализ значений слов в документе Word”) — она преобразует произвольную строку в строку с только прописными буквами.

см. также в приложении раздел “Преобразования регистра (функции LCase, UCase)”.

Функции Left и Right возвращают заданное число символов из строки-параметра, соответственно, слева и справа.

см. также в приложении раздел “Выделение подстроки”.

Функция Len возвращает число, равное длине строки-параметра.

см. также в приложении раздел “Форматирование строки”.

Таким образом, оператор соберет строковое значение из первого символа, преобразованного в верхний регистр и всех остальных символов кроме первого, никак не преобразованных.

В итоге можно записать исходный текст функции Сумма_прописью (листинг 4.1).

ЛИСТИНГ 4.1

Public Function Сумма_прописью(N As Currency) As String

Dim S, SUM As String

S = Format(N, "000000.00")

108 Глава 4. Практикум программирования на VBA для Excel и Word

SUM = ""

Select Case Mid(S, 1, 1)

Case "0":

Case "1": SUM = SUM + "сто "

Case "2": SUM = SUM + "двести "

Case "3": SUM = SUM + "триста "

Case "4": SUM = SUM + "четыреста "

Case "5": SUM = SUM + "пятьсот "

Case "6": SUM = SUM + "шестьсот "

Case "7": SUM = SUM + "семьсот "

Case "8": SUM = SUM + "восемьсот "

Case "9": SUM = SUM + "девятьсот "

End Select

If Mid(S, 2, 1) > "1" _

Or _

Mid(S, 2, 1) = "0" Then

Select Case Mid(S, 2, 1)

Case "2": SUM = SUM + "двадцать "

Case "3": SUM = SUM + "тридцать "

Case "4": SUM = SUM + "сорок "

Case "5": SUM = SUM + "пятьдесят "

Case "6": SUM = SUM + "шестьдесят "

Case "7": SUM = SUM + "семьдесят "

Case "8": SUM = SUM + "восемьдесят "

Case "9": SUM = SUM + "девяносто "

End Select

Select Case Mid(S, 3, 1)

Case "1": SUM = SUM + "одна "

Case "2": SUM = SUM + "две "

Case "3": SUM = SUM + "три " Case "4": SUM = SUM + "четыре " Case "5": SUM = SUM + "пять " Case "6": SUM = SUM + "шесть " Case "7": SUM = SUM + "семь " Case "8": SUM = SUM + "восемь " Case "9": SUM = SUM + "девять "

End Select

End If

If Mid(S, 2, 1) = "1" Then

Select Case Mid(S, 3, 1)

Case "0": SUM = SUM + "десять " Case "1": SUM = SUM + "одиннадцать " Case "2": SUM = SUM + "двенадцать " Case "3": SUM = SUM + "тринадцать "

Case "4": SUM = SUM + "четырнадцать "

Case "5": SUM = SUM + "пятнадцать "

Case "6": SUM = SUM + "шестнадцать "

Case "7": SUM = SUM + "семнадцать "

Сумма прописью 109

Case "8": SUM = SUM + "восемнадцать "

Case "9": SUM = SUM + "девятнадцать "

End Select

End If

If SUM <> "" Then

If Mid(S, 2, 1) = "1" Then SUM = SUM + "тысяч "

Else

Select Case Mid(S, 3, 1)

Case "1": SUM = SUM + "тысяча "

Case "2", "3", "4": SUM = SUM + "тысячи " Case Else: SUM = SUM + "тысяч "

End Select

End If

End If

Select Case Mid(S, 4, 1)

Case "0":

Case "1": SUM = SUM + "сто "

Case "2": SUM = SUM + "двести "

Case "3": SUM = SUM + "триста "

Case "4": SUM = SUM + "четыреста "

Case "5": SUM = SUM + "пятьсот "

Case "6": SUM = SUM + "шестьсот "

Case "7": SUM = SUM + "семьсот "

Case "8": SUM = SUM + "восемьсот "

Case "9": SUM = SUM + "девятьсот "

End Select

If Mid(S, 5, 1) > "1" _

Or _

Mid(S, 5, 1) = "0" Then

Select Case Mid(S, 5, 1)

Case "2": SUM = SUM + "двадцать "

Case "3": SUM = SUM + "тридцать "

Case "4": SUM = SUM + "сорок "

Case "5": SUM = SUM + "пятьдесят "

Case "6": SUM = SUM + "шестьдесят "

Case "7": SUM = SUM + "семьдесят "

Case "8": SUM = SUM + "восемьдесят "

Case "9": SUM = SUM + "девяносто "

End Select

Select Case Mid(S, 6, 1)

Case "1": SUM = SUM + "один "

Case "2": SUM = SUM + "два "

Case "3": SUM = SUM + "три "

Case "4": SUM = SUM + "четыре "

Case "5": SUM = SUM + "пять "

Case "6": SUM = SUM + "шесть "

Case "7": SUM = SUM + "семь "

Case "8": SUM = SUM + "восемь "

110 Глава 4. Практикум программирования на VBA для Excel и Word

Case "9": SUM = SUM + "девять "

End Select

End If

If Mid(S, 5, 1) = "1" Then

Select Case Mid(S, 6, 1)

Case "0": SUM = SUM + "десять "

Case "1": SUM = SUM + "одиннадцать "

Case "2": SUM = SUM + "двенадцать "

Case "3": SUM = SUM + "тринадцать "

Case "4": SUM = SUM + "четырнадцать "

Case "5": SUM = SUM + "пятнадцать "

Case "6": SUM = SUM + "шестнадцать "

Case "7": SUM = SUM + "семнадцать "

Case "8": SUM = SUM + "восемнадцать "

Case "9": SUM = SUM + "девятнадцать "

End Select

End If

SUM = SUM + " руб. " + Mid(S, 8, 2) + " коп."

Сумма_прописью = UCase(Left(SUM, 1)) + Right(SUM, Len(SUM) - 1)

End Function

Функция готова! Готова к чему? Можно использовать ее теперь в любом макросе, где она потребуется.

Использование функции Сумма_прописью()

Предположим, требуется автоматически формировать столбец “сумма прописью” по значениям столбца “сумма”, в который эти числовые значения либо вводятся пользователем, либо отображаются в него посредством связей Automation. Чтобы решить эту задачу, будем обрабатывать событие Change. При любом изменении значения в одной из ячеек листа будет сгенерировано это событие. Ссылка на ячейку при этом передается в процедуру обработки события посредством параметра Target.

Создание процедуры-обработчика события Change для листа рабочей книги Excel

Читатель уже без труда создаст процедуру-обработчик события Change для того листа рабочей книги, где такой обработчик требуется. Вот, какой исходный текст следует поместить в эту процедуру (листинг 4.2).

ЛИСТИНГ 4.2

Private Sub Worksheet_Change(ByVal Target As Range)

On Error Resume Next

If Target.Column = 1 Then

Target.Offset(0, 1).Value = Сумма_прописью(Target.Value)

End If

End Sub

Сумма прописью 111

Параметр Target указывает на изменившуюся ячейку. А свойство Offset(0, 1) этой ячейки указывает на ячейку справа от нее. (Нетрудно догадаться, что, скажем, выражение Offset(1, 2) указывало бы на ячейку, находящуюся одной строкой ниже и двумя столбцами правее). И вот, если речь идет о ячейке первого столбца (Column = 1), то в ячейку справа поместим значение, возвращенное функцией Сумма_прописью. Разумеется, предполагается, что столбцы, о которых идет речь, отформатированы надлежащим образом — для ввода денежных и текстовых значений соответственно. Теперь можно проверить работоспособность макроса путем ввода различных числовых значений в первый столбец. Вернемся в окно MS Excel и введем значения в первый столбец того листа, для которого был создан обработчик события Change. Результат изображен на рис. 4.2.

Рис. 4.2. Строки с суммой прописью формируются автоматически в столбце B

Защита от возникновения каскадных событий

ПРИМЕЧАНИЕ

Остается прояснить маленький вопрос — зачем в начале процедуры использована строка On

Error Resume Next, защищающая макрос от прерываний при возникновении ошибок. Какие ошибки могут тут возникнуть? И еще: зачем проверять номер столбца?

Дело в том, что событие Change возникает практически при любом изменении значения ячейки. И если просто прочитать значение некоторой ячейки и записать возвращенное функцией значение в ячейку справа, то возникнет так называемое каскадное событие. Обработка события Change для первой ячейки еще не завершилось, но уже возникает событие Change для ячейки справа (ведь в нее записано новое значение!), которое, в свою очередь, вызовет событие Change для следующей ячейки справа и так далее. Факт обработки события является в данном случае причиной возникновения такого же события (это, кстати сказать, классическая причина возникновения каскадных событий).

В результате возникнет “бесконечная” последовательность событий, которая приведет к аварийному прерыванию процедуры. Такая картина событий могла быть, если бы функция возвращала значение такого же типа, что и прочитанное в первой ячейке, или, если бы не был указан явным образом тип параметра и тип возвращаемого значения. В данном случае уже самое первое излиш-

нее событие Change приведет к ошибке, поскольку функции Сумма_прописью будет передано строковое значение из второго столбца (а ожидает она, как указано, числовое денежное значение типа Currency). Благодаря этому обстоятельству, а также благодаря строке On Error Resume

112 Глава 4. Практикум программирования на VBA для Excel и Word

Next цепочка каскадного события в нашем случае оборвется в самом начале. Но общий способ “предохранения” от каскадных событий состоит в том, чтобы ограничить сферу действия процедуры достаточно узким диапазоном. Для этого и наложено условие If Target.Column = 1 Then… (если изменившаяся ячейка принадлежит столбцу 1, то).

см. также в гл. 3 раздел “Управление ошибками выполнения макроса”.

Частотный словарь

Вряд ли эту задачу можно отнести к часто встречающимся проблемам современного офиса. И все же, автоматическое составление частотного словаря не лишено практического смысла в некоторых ситуациях, когда имеет значение именно словарный состав документа. Главная же причина, по которой задача “Частотный словарь” включена в состав данной главы, заключается в том, что для ее решения необходимо будет широко использовать разнообразные возможности языка VBA, и, в то же самое время, достаточно глубоко “заглянуть” в объектную модель документа Word. Словом, решение этой задачи — хорошая практика для человека, намеревающегося работать с документами Word посредством макросов VBA.

Постановка задачи

Итак, в чем же, собственно, заключается задача? Необходимо написать макрос, который составлял бы частотный словарь документа Word. Словарь должен состоять из заданного числа слов, наиболее часто встречающихся в документе. Например, 10 или 500 наиболее употребительных в данном документе слов. Конечно, отобранные слова необходимо упорядочить в порядке убывания их частоты — это обычный принцип построения частотных словарей. В какой форме должен быть создан словарь? Разумеется, в форме нового документа Word — работая в среде Word было бы странно избрать какой-то иной путь.

Каким образом будет запускаться будущий макрос? На этот раз самым, что ни на есть, обыкновенным способом. Это будет просто макрос с именем “Частотный_словарь”, который можно будет запустить при помощи команды Сервис | Макрос | Макросы. Вначале необходимо создать его, введя в поле Èìÿ строку “Частотный_словарь” и щелкнув на кнопке Создать. А затем, после того, как исходный текст макроса будет введен, его можно будет выполнить при помощи той же команды Сервис | Макрос | Макросы — достаточно выделить в списке имя “Частотный_словарь” и щелкнуть на кнопке Выполнить.

Разрабатываем исходный текст макроса

Зададимся вначале параметрами словаря. Дело в том, что для подсчета числа вхождений слова в текст документа нам потребуется где-то хранить все встреченные отличающиеся друг от друга слова. Неизбежно поэтому использование массива строковых значений, то есть специальной переменной, к однородным элементам которой можно обращаться по их порядковым номерам.

Использование массивов

Размерность буферного массива имеет большое значение. Не известно, сколько разных слов может содержаться в документе, поэтому идеальный массив для подсчета слов должен бы был являться бесконечным. Увы, это невозможно, и нам придется пойти на компромисс. Допустим, что самый большой документ, который может встретиться, состоит из 5000 слов. Тогда вполне разумным выглядит предположение, что предстоит иметь дело с не более чем 3000 разных слов. Пусть массив строк для хранения подсчитываемых слов имеет размерность 3000. Размер словаря можно задать произвольным образом — пусть он состоит из 200 наиболее часто встречающихся слов.

Объявление констант

Эти две величины имеет смысл объявить в качестве констант, тогда их нетрудно будет изменить — достаточно исправить значение в объявлении константы и соответствующая величина изменится повсюду в программе:

Соседние файлы в папке Книги