В первой части статьи мы приступили к работе по исследованию языка Visual Basic for Applications, записали первую макрокоманду и начали изучение ее текста, а здесь оно будет продолжено.

Selection.Find.ClearFormatting
Selection.Find.ParagraphFormat.Alignment
 = wdAlignParagraphLeft
Selection.Find.Replacement.ClearFormatting
Selection.Find.Replacement.ParagraphFormat
.Alignment = wdAlignParagraphJustify
With Selection.Find
.Text = «»
.Replacement.Text = «»
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace
:=wdReplaceAll

Для выравнивания абзацев по ширине при записи макроса была вызвана функция замены Word. Ее вызов и был записан на языке VBA. Строка Selection.Find.ClearFormatting, видимо, предназначена для снятия параметров форматирования в окне замены — эквивалент нажатию кнопки «Снять форматирование», а строки Selection.Find. ParagraphFormat.Alignment = wdAlignParagraphLeft и Selection. Find.Replacement.ParagraphFormat. Alignment = wdAlignParagraphJustify определены для установки параметров того, что надо искать (выравнивание Left, т. е. «По левому краю»), и того, на что следует заменить (выравнивание Justify, т. е. «По ширине»). Команды впрямую соответствуют переводу слов с английского языка на русский: Selection — выделение, Find.Replacement — найти и заменить, ParagraphFormat — формат абзаца, Alignment — выравнивание, AlignParagraphJustify — равнять абзац по ширине. С оператором With мы уже знакомы, поэтому в следующем фрагменте речь пойдет об окне «Найти и заменить».

Вызов функции «Найти и заменить» на языке Visual Basic for Applications

Легко догадаться, что параметры объекта Selection.Find (т. е. поиск по всему тексту или по его выделенной части) соответствуют параметрам окна «Найти и заменить»: .MatchWholeWord — искать целое слово; .MatchCase — учитывать регистр; .Text — заменяемый текст, а .Replacement.Text — сообщить, на что заменить. В рассматриваемом примере заменяется не текст, а форматирование, поэтому строки с этими параметрами заполнять не нужно.

Заметив в первых четырех строках повторение слов Selection.Find, можно счесть, что и их содержимое допускается включить в блок оператора With. Проверим, так ли это. Ниже последнего записанного макроса введем строку Sub experience1() — имя после Sub может быть любым, важно лишь, чтобы оно не совпадало с какой-либо командой VBA. Нажмем «Ввод» — через две строки редактор VBA поставит End Sub. С помощью команд «Копировать» и «Вставить» скопируем из нашей программы в текст нового макроса блок With Selection.Find, а затем переместим все строки, отражающие в макросе «работу» окна «Найти и заменить», в блок With Selection.Find, убрав при этом слова Selection.Find.

Sub experience1()
With Selection.Find
.ClearFormatting
.ParagraphFormat.Alignment
 = wdAlignParagraphLeft
.Replacement.ClearFormatting
.Replacement.ParagraphFormat
.Alignment = wdAlignParagraphJustify
.Text = «»
.Replacement.Text = «»
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute Replace:=wdReplaceAll
End With
End Sub

Теперь испытаем программу: откроем из общего набора еще один документ и, нажав клавишу , запустим новый макрос (курсор должен стоять внутри текста запускаемого макроса). Замена произошла так, как требуется, значит, можно сокращать текст программы описанным выше способом.

Раскроем значение еще нескольких интересных строк, смысл которых может вызвать вопросы: .Forward = True, .Wrap = wdFindContinue, .Execute Replace:=wdReplaceAll. В первой строке указывается направление поиска — вперед или назад (в последнем случае стояло бы значение False). Но что же скрывает вторая? Чтобы выяснить это, напишем еще один макрос с такой же заменой выравнивания, но с разными параметрами направления поиска (табл. 1).

Вот так можно определить возможные значения параметра

Вполне очевидно, что наличие или отсутствие согласия на продолжение поиска не влияет на записываемый текст. Тогда как же отказаться от запроса на продолжение? Откроем текст макроса и поставим еще один знак «=» после команды .Wrap.

Теперь все ясно: чтобы отказаться от замены в конце документа, нужно обозначить параметр .Wrap как wdFindStop (табл. 2).

Справка по слову Execute

Осталось разобраться со строкой .Execute Replace:=wdReplace All. Поскольку слово Execute переводится как «исполнить», оно должно означать команду запуска поиска. Обратимся к справочной системе.

Из четырех предложенных ссылок — Dialog, Find, KeyBinding, MailMerge — нас интересует Find, поскольку мы работаем именно с этим объектом.

Справка по команде Execute объекта Find

Так и есть, Execute запускает команду поиска и замены на выполнение: Runs the specified find operation. Returns True if the find operation is successful. Из текста справки выясняется, что все параметры поиска и замены можно задать также в скобках, а кроме того, записать в переменную результат выполнения команды — успешно ли прошел поиск (т. е. были ли найдены в данном сеансе работы команды «Найти и заменить» искомые элементы). В данном случае этого не требуется, однако запомнить ее все же стоит.

Продолжим изучение записанной макрокоманды.

ActiveDocument.Save

Вопросов не вызывает — это команда сохранения активного документа.

ActiveDocument.SaveAs FileName
:=?Доклад1.rtf?, FileFormat:=wdFormatRTF,
 LockComments:=False, Password:=??,
 AddToRecentFiles:=True, WritePassword
:=??, ReadOnlyRecommended:=False,
 EmbedTrueTypeFonts:=False,
 SaveNativePictureFormat:=False,
 SaveFormsData:=False, SaveAsAOCELetter
:= False

А вот здесь уже потребуется редактирование. Это команда «Сохранить как...» — поскольку соответствующая опция Word задается в диалоговом окне, то в текст макроса помещаются все возможные параметры, как соответствующие установленным по умолчанию, так и нет: AddToRecentFiles — добавлять ли ярлык в папку Recent, EmbedTrueTypeFonts — внедрять ли шрифты TrueType. Наиболее интересны элементы FileName:=?Доклад1.rtf? и FileFormat:=wdFormatRTF — имя сохраняемого активного документа и его формат соответственно. Последний, Rtf, нас устраивает. А вот как быть с именем? Ведь у каждого файла оно должно быть свое, уникальное. Сначала попробуем из текста данной команды просто исключить строку с указанием имени. Создадим еще один модуль, скопируем туда полную команду ActiveDocument.SaveAs со всеми параметрами и удалим строку FileName:=?Доклад1.rtf?. После запуска программы ничего не произошло, однако она все же что-то делала, ведь и жесткий диск работал, и в течение нескольких секунд система была недоступна, да и сообщений об ошибках не появилось. Проверим, что делалось. Откроем в Блокноте или другом текстовом редакторе файл, который сохранялся, — в данном случае это файл «Доклад2.doc», — предварительно выгрузив его из Word.

Откроем в Блокноте файл, который сохранялся командой ActiveDocument.SaveAs

Так вот в чем дело — файл был сохранен в формате Rtf, но под прежним именем и с расширением .doc. Но нас это не устраивает, поэтому имя пусть останется то же, а вот расширение должно быть .rtf.

А что произойдет, если запустить программу при активном и ни разу не сохранявшемся документе? Проделаем это. Активный документ получит имя Doc1.doc (или Doc2.doc, Doc3.doc — у кого как) и окажется либо в папке, где имело место последнее сохранение вручную какого-либо файла, либо в папке «Мои документы», предназначенной по умолчанию для файлов Word, либо в любой другой заданной вами (путь «по умолчанию» можно посмотреть, выбрав пункты «Сервис?Параметры?Расположение».

Здесь для документов Word можно посмотреть путь к папке по умолчанию

Таким образом, если программно сохранять ранее не сохранявшийся документ, не задавая ему имя и место расположения (впрочем, последнее можно указать, включив в пункт FileName полный путь к нему), то активный файл сохранится в папке и с именем по умолчанию (либо там, где был последний раз сохранен вручную какой-нибудь документ).

Но как же быть с нашей программой? Ведь требуется, чтобы все обрабатываемые файлы сохранялись со своими именами и с расширением .rtf. Попробуем получить имя активного документа программно. Создадим еще один макрос, введем в него параметр ActiveDocument, поставим точку и увидим, что среди возможных продолжений команды есть свойство Name.

Так можно найти нужное продолжение команды

Проверим работу. Модифицируем экспериментальную процедуру следующим образом.

Sub experience2()
MsgBox ActiveDocument.Name
End Sub
Результат работы команды MsgBox ActiveDocument.Name

Это даст возможность вызвать окно сообщения со значением функции ActiveDocument.Name. Конечно, можно было бы написать Debug.Print ActiveDocument.Name, что позволило бы отобразить значение данной функции в специальном «Окне отладки» (из меню редактора VBA вызывается по команде «Вид?Окно отладки»), но работать с окном сообщения как-то привычнее. Итак, нажимаем и получаем «Доклад1.doc».

Однако нам требуется только одно имя, без расширения. Проще всего, наверное, убрать последние четыре символа, а именно .doc. Снова используем справочную систему. Находясь в VBA, нажимаем , в открывшемся диалоговом окне — кнопку «Разделы» и далее — «Предметный указатель», где все разделы справочной системы классифицированы по смыслу.

Чтобы вывести на экран основное справочное меню, нажмите эту кнопку
Предметный указатель

Теперь в поле поиска введем слово «строка». В списке разделов справочной системы Visual Basic for Applications выберем «крайние левые символы». Получаем справку по функции Left.

Справка по функции Left

Превосходно — функция Left является именно той, которая нам необходима. Но она требует указать количество символов во всей строке — как это узнать? Для этого воспользуемся, как описано в справке, функцией Len. Ознакомимся с последней, щелкнув на ссылке «См. также» и выбрав из списка строку «Функция Len».

Справка по функции Len

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

imyadoc = Left(ActiveDocument.Name, 
Len(ActiveDocument.Name) - 4)

Для проверки создадим и запустим еще один модуль, после чего в окне сообщения появится имя документа без расширения.

Sub experience3()
imyadoc = Left(ActiveDocument.Name,
 Len(ActiveDocument.Name) - 4)
MsgBox imyadoc 
End Sub
Результат работы команды MsgBox Left(ActiveDocument.Name, Len(ActiveDocument.Name) - 4)

Все получилось таким образом, как и требовалось.

Следовательно, команда программы ActiveDocument.SaveAs должна выглядеть так.

ActiveDocument.SaveAs FileName : = Left(ActiveDocument.Name, Len(ActiveDocument.Name) - 4), FileFormat:=wdFormatRTF, LockComments :=False, Password:=??, AddToRecentFiles :=True, WritePassword:=??, ReadOnlyRecommended:=False, EmbedTrueTypeFonts:=False, SaveNativePictureFormat:=False, SaveFormsData:=False, SaveAsAOCELetter:= False

Лишнее можно убрать, хотя и не обязательно. Получится:

ActiveDocument.SaveAs FileName
: = Left(ActiveDocument.Name,
 Len(ActiveDocument.Name) - 4),
 FileFormat:=wdFormatRTF

Было бы также хорошо, если бы новый документ в формате Rtf сохранялся в той же папке, что и исходный. Для этого к параметру FileName необходимо добавить информацию о пути к активному документу.

ActiveDocument.SaveAs
 FileName:=ActiveDocument.Path
 + ?? + Left(ActiveDocument.Name,
 Len(ActiveDocument.Name) - 4),
 FileFormat:=wdFormatRTF

И теперь последняя строка.

ActiveWindow.Close
End Sub

Вот и конец программы — закрытие активного окна с документом. Анализ текста завершен.

Орлов Антон Александрович, antorlov@inbox.ru, http://antorlov.chat.ru

Продолжение в следующем номере.


Таблица 1
Параметры поиска Записанный фрагмент текста
Вперед, с согласием на продолжение поиска.Forward = True.Wrap = wdFindAsk
Назад, с согласием на продолжение поиска.Forward = False.Wrap = wdFindAsk
Вперед, без согласия на продолжение поиска.Forward = True.Wrap = wdFindAsk
Назад, без согласия на продолжение поиска.Forward = False.Wrap = wdFindAsk
Везде.Forward = True.Wrap = wdFindContinue

назад

Таблица 2
Параметры поиска Необходимый фрагмент кода
Вперед, с согласием на продолжение поиска.Forward = True.Wrap = wdFindAsk
Назад, с согласием на продолжение поиска.Forward = False.Wrap = wdFindAsk
Вперед, без согласия на продолжение поиска.Forward = True.Wrap = wdFindStop
Назад, без согласия на продолжение поиска.Forward = False.Wrap = wdFindStop
Везде.Forward = True.Wrap = wdFindContinue

назад


Небольшое отступление

Изложенный здесь способ задания имени активного документа без расширения, увы, не самый лучший. Есть еще одна возможность узнать только имя документа, унаследованная из предыдущих версий Word, — в Microsoft Word 6.0 и 95 имеется специальная команда, к сожалению, не включенная в VBA, но ей можно воспользоваться.

В Word версии 6.0 и 95 была включена функция «FileNameInfo$(документ, параметр)», позволяющая задать имя документа без расширения, а также путь к нему с именем и без него. Чтобы узнать одно имя документа без расширения и пути, параметр должен быть равен 4. (Все это подробно описано в справочной системе по WordBasic для Word 6.0 и 95.) В VBA эта функция выглядит так:

imyadoc = WordBasic.[FileNameInfo$]
(ActiveDocument.Name, 4)

При обработке документов с расширениями, состоящими более чем из трех символов, например .html, данная функция будет просто незаменима, позволяя заметно упростить текст программы. Однако те, кто только начинает работать с Visual Basic for Applications, данную функцию могут упустить, так как знает о ней самой и тем более о ее параметрах обычно лишь тот, кто раньше уже имел дело с WordBasic. Поэтому в рассматриваемой программе мы будем использовать функцию imyadoc = Left(ActiveDocument.Name, Len(ActiveDocument.Name) - 4).