Delphi word замена текста
← →
sapsi ( 2005-02-04 13:20 ) [0]
Добрый день.
Как реализовать сабж?
Делаю примерно следующее:
wordapp:=CreateOleObject("word.Application");
worddoc:=wordApp.documents.Add;
wordapp.Selection.WholeStory;
worddoc.Content.Find.ClearFormatting;
worddoc.Content.Find.Replacement.ClearFormatting;
worddoc.Content.Find.Text:="111";
worddoc.Content.Find.Replacement.Text:="222";
worddoc.Content.Find.MatchCase:= False;
worddoc.Content.Find.Execute(wdReplaceAll);
не ищет "111", пробовал и по-другому, что-то вроде:
Все равно не работает.
Кто подскажет почему?
Спасибо.
Alezy © ( 2005-02-04 17:19 ) [1]
Надо просто пользоваться правильными свойствами объектной модели MS Word. Ниже приведен пример замены, реализованный на VBA, думаю перевести его на Delphi не составит никакого труда. Вообще автоматизацию офиса лучше изучать с помощью VBA и переносить готовые куски кода в другие среды разработки.
Sub TestReplace()
Dim wordApp As Word.Application
Dim worddoc As Word.Document
Set wordApp = CreateObject("word.Application") "создаем объект Word
Set worddoc = wordApp.Documents.Add "создаем новый документ
With wordApp.Selection "добавляем в документ данные.
.TypeText Text:="Test1111Test"
.TypeParagraph
.TypeText Text:="Test2222Test"
.TypeParagraph
.TypeText Text:="Test3333Test"
End With
With wordApp.Selection.Find "заполняем опции замены
.ClearFormatting
.Replacement.ClearFormatting
.Text = "111"
.Replacement.Text = "222"
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute Replace:=wdReplaceAll "меняем всё
End With
worddoc.SaveAs ("c:\AAA.doc") "сохраняем результаты или получем их другим способом
worddoc.Close "закрываем документ.
Set wordApp = Nothing "выгружаем Word
End Sub
GanibalLector © ( 2005-02-06 22:31 ) [2]
var WordApp:OleVariant;
begin
WordApp := CreateOleObject("Word.Application");
try
WordApp.Documents.Open("C:\2.doc");
wordapp.visible:=true;
WordApp.Selection.Find.ClearFormatting;
WordApp.Selection.Find.Replacement.ClearFormatting;
WordApp.Selection.Find.Replacement.Text:="GanibalLector";
WordApp.Selection.Find.Text := "L";
WordApp.Selection.Find.Forward := True;
WordApp.Selection.Find.Wrap := 1;
WordApp.Selection.Find.Format := False;
WordApp.Selection.Find.MatchCase := False;
WordApp.Selection.Find.MatchWholeWord := False;
WordApp.Selection.Find.MatchWildcards := False;
WordApp.Selection.Find.MatchSoundsLike := False;
WordApp.Selection.Find.MatchAllWordForms := False;
В предыдущей заметке "Поиск и замена текста в документе MS Word из Delphi" я рассказывал, как дорабатывал старый модуль, который генерирует клиентам компании письма в формате MS Word с помощью поиска и замены текста. Сдав модуль заказчикам, я в свободное от работы время, переделал его. Вместо поиска и замены использовал поля с переменными (DocVariable).
В шаблон письма с помощью макроса добавил переменные
Sub AddFields()
ThisDocument.Variables.Add "FIO", "FIO"
ThisDocument.Variables.Add "ADDRESS", "ADDRESS"
.
End Sub
и расставил поля по тексту шаблона. В макросе у метода Add первый параметр – название переменной, а второй – ее значение. Я специально сделал их одинаковыми, чтобы пользователям было проще и нагляднее редактировать шаблоны.
Затем внес изменения в методы модуля работающие с MS Word. Если опустить все детали и различную логику, то упрощенно работа с MS Word выглядит так:
Var
wa: WordApplication;
ovDotName, ovFileName: OleVariant;
i: Integer;
q: TSDQuery;
.
begin
.
wa := CreateComObject(CLASS_WordApplication) as _Application;
ovDotName := 'какой то шаблон.dot';
wa.Documents.Add(ovDotName, EmptyParam, EmptyParam, EmptyParam);
For i := 0 to q.FieldCount-1 do
MSWordSetVariable(q.Fields[i].FieldName, q.Fields[i].AsString);
.
ovFileName := 'письмо любимому клиенту.doc';
wa.ActiveDocument.SaveAs(ovFileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
.
procedure MSWordSetVariable(ovVariable: OleVariant; const sValue: String);
begin
If sValue = ''
then wa.ActiveDocument.Variables.Item(ovVariable).Value := ' '
else wa.ActiveDocument.Variables.Item(ovVariable).Value := sValue
end;
Если переменной MS Word присвоить пустую строчку, то в поле выводится текст 'Ошибка! Переменная документа не указана.', поэтому вместо пустого значения я присваиваю ей пробел (так же делает и сам MS Word). Это связано со странной работой MS Word с коллекцией переменных. Если переменной присвоить пустую строку, то количество переменных в коллекции (Variables.Count) уменьшается на единицу, а попытка удалить переменную (Variables.Item(ovVariable).Delete) дает ошибку 'Объект был удален', т.е. присвоение пустой строки равносильно удалению. При этом после удаления переменной из коллекции переменных, присвоение ей непустого значения выполняется успешно и количество переменных в коллекции увеличивается на единицу, т.е. присвоение непустого значения равносильно вызову метода добавления переменной в коллекцию переменных.
Продемонстрирую вышесказанное примером кода, по которому видно как изменяется количество переменных (iCount):
iCount := wa.ActiveDocument.Variables.Count; // iCount = 3
wa.ActiveDocument.Variables.Item(ovVariable).Value := '';
iCount := wa.ActiveDocument.Variables.Count; // iCount = 2
wa.ActiveDocument.Variables.Item(ovVariable).Value := 'не пусто';
iCount := wa.ActiveDocument.Variables.Count; // iCount = 3
wa.ActiveDocument.Variables.Item(ovVariable).Delete;
iCount := wa.ActiveDocument.Variables.Count; // iCount = 2
wa.ActiveDocument.Variables.Item(ovVariable).Value := 'не пусто';
iCount := wa.ActiveDocument.Variables.Count; // iCount = 3
wa.ActiveDocument.Variables.Item(ovVariable).Value := '';
iCount := wa.ActiveDocument.Variables.Count; // iCount = 2
wa.ActiveDocument.Variables.Item(ovVariable).Delete; // ошибка 'Объект был удален'
Вот такая логика у индусов, писавших этот кусок MS Word.
После присвоения значений всем переменным, осталось только дать команду полям обновиться новыми значениями переменных. Для этого в VBA у коллекции объектов полей (Fields) есть метод Update:
А для того, чтобы избежать дальнейшего обновления полей, избавимся от их связи с переменными:
У каждого структурного элемента документа (заметки, колонтитула, сноски и т.д.) своя коллекция объектов полей, поэтому, если документ, как у меня, состоит из разных структурных элементов, то методы Update и Unlink необходимо вызвать для каждого из этих элементов. Для этого перебираем все элементы коллекции StoryRanges.
procedure MSWordUpdateStoryRanges;
Var
StoryRanges: Word2000.StoryRanges;
StoryRange: Word2000.Range;
iStoryIndex: integer;
Begin
StoryRanges := wa.ActiveDocument.StoryRanges;
For iStoryIndex := wdMainTextStory to wdFirstPageFooterStory do
Try
StoryRange := StoryRanges.Item(iStoryIndex);
If StoryRange <> nil then
begin
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
While (StoryRange.NextStoryRange <> nil) do
begin
StoryRange := StoryRange.NextStoryRange;
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
end;
end;
Except
StoryRange := nil;
End;
End;
Т.к. в моем шаблоне есть только основной текст и одна заметка, то вместо процедуры, которая перебирает все элементы коллекции StoryRanges, я сделал процедуру, которая работает только с одним ее элементом:
procedure MSWordUpdateStoryRange(const iStoryIndex: integer);
Var
StoryRange: Word2000.Range;
begin
Try
StoryRange := wa.ActiveDocument.StoryRanges.Item(iStoryIndex);
If StoryRange <> nil then
begin
StoryRange.Fields.Update;
StoryRange.Fields.Unlink;
end;
Except
End;
end;
И вызываю ее перед сохранением документа.
Все бы ничего, но часть текста передаваемого из программы в MS Word необходимо было выводить жирным шрифтом. А это через переменные не сделать :( В подобном случае "поиск и замену текста" можно заменить на "переход к закладке и вывод текста". Например, в шаблон вставляем закладку с именем 'писать текст сюда', а в программе пишем:
Var
ovBookmarkName, ovWhat: OLEVariant;
begin
ovWhat := wdGoToBookmark;
ovBookmarkName := 'писать текст сюда';
Try
If wa.Selection.GoTo_(ovWhat, EmptyParam, EmptyParam, ovBookmarkName) <> nil then
begin
wa.Selection.TypeText('просто текст ');
wa.Selection.Font.Bold := 1;
wa.Selection.TypeText('жирный текст');
wa.Selection.Font.Bold := 0;
wa.Selection.TypeText(' просто текст');
end;
Except
End;
Вот и все :)
Раннее связывание и использование полей/закладок дало существенный рост скорости генерации писем. Если на 500-х различных документах этого было почти не заметно, то при генерации 15 000 документов, прирост скорости составил 30% (специально проверил несколько раз на одних и тех же исходных данных).
P.S. При работе с ранним связыванием мне не нравится только одно – многие параметры в методах объявлены, как VAR (даже индекс элемента коллекции!), поэтому приходится заводить для них специальную переменную типа OleVariant.
Сегодня, в последний рабочий день недели, практически весь день провозился над передачей данных из Delphi в Word. Так как подозрение есть, что работа продолжится то решил кое-какие моменты по работе с Microsoft Word в Delphi запечатлеть и у себя в блоге. Написать такую мини-шпаргалку (тем более, что по Excel уже кое что есть).
Для начала, немного общих моментов по работе с MS Office в Delphi. И первое, что мы сделаем — это создадим объект Word.Application. Создается этот объект абсолютно также, как и объект Excel.Application:
Всё достаточно просто. Далее мы можем работать с объектом следующим образом:
- Создавать документ Word с нуля
- Открыть уже существующий документ и изменить в нем текст для получения необходимой формы документа.
Рассмотрим оба варианта, т.к. оба они имеют как свои плюсы, так и недостатки.
1. Создание документа Microsoft Word в Delphi с нуля.
Чтобы создать новый документ необходимо выполнить метод Add у коллекции Documents, т.е.:
и после этой операции уже начинать работать с документам обращаясь к нему по индексу или имени в коллекции. Также, можно создать новый документ по шаблону (*.dot). Для этого необходимо выполнить тот же метод Add, но с одним входным параметром — путем к файлу-шаблону:
Чтобы получить список всех открытых в данный момент документов Word можно воспользоваться следующим листингом:
Обратите внимание, что нумерация начинается с 1, а не с нуля. Чтобы активировать любой документ из коллекции для работы, необходимо выполнить метод Activate:
где index — номер документа в коллекции.
Теперь можно приступать к записи и чтению документа. Для работы с текстов в документе Word, как и в Excel для работы с ячейками таблицы, определен объект Range. Именно методы этого объекта и дают нам возможность работы с текстом. Для начала рассмотрим работу двух основных методов: InsertBefore и InsertAfter.
Как следует из название — первый метод вставляет текст в начало содержимого Range, а второй — в конец. При этом сам объект Range может содержать как весть документ (Document) так и какую-либо его часть. Например, в следующем листинге я вставлю строку в начало документа и затем методом InsertAfter буду добавлять несколько строк текста в конец документа:
При выполнении этих трех операции Range содержал весь документ.
Если работать со всем документом неудобно, а необходимо, например выделить фрагмент с 50 по 100 символ и работать с ним, то можно воспользоваться функцией Range, которая вернет нам необходимый объект Range:
Это что касается записи текста. Решение обратной задачи — чтения текста из документа ещё проще. Достаточно воспользоваться свойством Text у объекта Range:
Также для чтения документа можно воспользоваться коллекцией документа Words (слова). За слово принимается непрерывный набор символов — цифр и букв, который оканчивается пробелом.
Перечисляются слова документа точно также как и при работе с коллекцией документов, т.е. первое слово имеет индекс 1 последнее — Word.Count.
В данном случае я вывел на экран последнее слово в документе.
Очевидно, что приведенный выше способ работы с документам хорош в случае, когда требуется создать относительно простой документ Word и не требуется лишний раз рассчитывать фрагменты текста, правильно вставлять таблицы и т.д. Если же необходимо работать с документами, которые имеют сложное содержание, например текст в перемежку с рисунками, таблицами, а сам текст выводится различными шрифтами, то, на мой взгляд наиболее удобно использовать второй способ работы с Word в Delphi — просто заменить текст в уже заранее заготовленном документа.
2. Работа с документами Word в Delphi. Открытие готового документа и замена текста.
Чтобы открыть заранее заготовленный документ Word в Delphi достаточно воспользоваться методом Open у коллекции Documents, например так:
Метод Open можно вызывать с несколькими аргументами:
- FileName: string — путь и имя файла;
- ConfirmConversions: boolean — False — не открывать диалоговое окно «Преобразование файла» при открытии файла, формат которого не соответствует формату Word (doc или docx)
- ReadOnly:boolean — True — открыть документ в режиме «Только для чтения»
- AddToRecentFiles: boolean — True, чтобы добавить документ в список недавно открытых документов.
- PasswordDocument: string — пароль для открытия документа
- PasswordTemplate: string — пароль для открытия шаблона
- Revert : boolean — True, чтобы вернуться к сохраненному документу, если этот документ открывается повторно.
- WritePasswordDocument: string — пароль для сохранения измененного документа в файле
- WritePasswordTemplate:string — пароль для сохранения изменений в шаблоне
- Format:integer — формат открываемого документа.
Обязательным параметром метода Open является только FileName, остальные — могут отсутствовать. Если же Вам необходимо воспользоваться несколькими параметрами, то их необходимо явно указывать при вызове метода, например:
В этом случае документ открывается в режиме «Только для чтения». При таком способе вызова (с явным указанием аргументов) положение аргументов может быть произвольным.
Что касается последнего аргумента — Format, то он может принимать целочисленные значения (применительно к версиям Microsoft Word 2007 и выше) от 0 до 13. При этом, для того, чтобы открыть «родные» вордовские документы (doc) достаточно использовать значения 0 или 6.
Приведенная выше функция позволяет провести поиск и замену текстового фрагмента во всём документе. Для того, чтобы ограничить возможности пользователя при работе с шаблоном документа я обычно ставлю на необработанный файл пароль, а после обработки — пароль снимаю и сохраняю документ с другим названием в необходимую директорию.
Вот, наверное, самые-самые простые методы работы с Word в Delphi. Кстати, пишу пост и, думаю, что у кого-то из читателей может возникнуть вопрос: причём тут Delphi в Internet и Word в Delphi? :) Честно говоря, приведенный выше фрагменты кода можно использовать для нужд в Internet с натяжкой, например, при автосоставлении небольших отчётов по чему-либо. А вообще, в недалеком будущем, есть в планах поразбираться с Тезаурусом Word и попробовать составить небольшой синонимайзер для собственных нужд — он-то и пригодится нам в Internet :)
Приведенным ниже кодом, ищу и заменяю текст в документе word:
function SeekInsertInText(SText, IText : string):boolean;
begin
WordApp1.Selection.Find.ClearFormat ting;
WordApp1.Selection.Find.Replacement .ClearFormatting;
WordApp1.Selection.Find.Text:=SText ;
WordApp1.Selection.Find.Replacement .Text:=IText;
WordApp1.Selection.Find.Forward:=Tr ue;
WordApp1.Selection.Find.Wrap:=wdFin dContinue;
WordApp1.Selection.Find.Format:=Fal se;
WordApp1.Selection.Find.MatchCase:= False;
WordApp1.Selection.Find.MatchWholeW ord:=False;
WordApp1.Selection.Find.MatchWildca rds:=False;
WordApp1.Selection.Find.MatchAllWor dForms:=False;
WordApp1.Selection.Find.Execute(Rep lace:=wdReplaceAll);
end;
Всё хорошо ищет и заменяет.
НО! моя функция игнорирует надписи. Т.к. пока я дилетант во взаимодействии word'a и delphi, подскажите как можно решить эту проблему.
Для непросвещенных объясняю, что такое "надпись" в word скриншотом, который пониже.
И чтобы сразу выкинуть мусор и хлам из темы просьба не писать "умным":
- погугли, там полно примеров;
- запиши макрос в word и посмотри;
- не могу понять что ты хочешь;
ЗАРАНЕЕ, ОГРОМНОЕ СПАСИБО!
а вот и обещанный скриншот.
Ни у кого ни каких идей?
Думал решить проблему запуском готового макроса программно.
Но тогда придется писать макрос для каждых поиска и замены. А в моей программульке это невероятно большой объем.
Следовательно назревает вопрос - можно ли ввести параметры замены и поиска в делфи, а затем с этими параметрами запустить макрос?
Конечно ответ скорей всего - нет.
Но может это натолкнет кого то на какие-то мысли.
активность форумчан просто поражает)) Наверно не там я свою проблему описал(
Ну что же, продолжим беседовать самим с собой. Решение точно найду может кому то потом пригодится)
реализовал идею с макросами.
но опять НО.
для ясности сначала:
вот такой функцией создаю макрос - поиска и замены в word.
описать поэтапно что делает функция не могу, так как нашел данный код во всемирной паутине, содрал, немного изменил под себя.
В двух словах:
1. описываем макрос;
2. создаем кнопку макроса;
3. нажимаем на нее;
4. ищем заменяем;
5. удаляем кнопку и макрос;
как то так.
знаю что пункты 2, 3, 4 лишние, но во избежании еще большей путаницы, оставил все как есть.
теперь ПРОБЛЕМА.
после запуска программки и соответствующих действий, выдает ошибку
Method 'VBProject' not supported by automation object.
как это переводится я знаю (кстати если заменить слово object на abject, интересный перевод получается)))
но что это значит и как его приготовить я не понимаю.
О МОЛЧАЛИВЫЕ МУДРЕЦЫ окиньте взором своим текст мой, и ПОМОГИТЕ.
← →Sesh ( 2003-07-26 08:39 ) [0]
Сталкнулся с надобностью замены текста в Word с Delphi, как это реализовать? Киньте маленький примерчик.
← →Leran2003 ( 2003-07-26 08:55 ) [1]
WordApp: TWordApplication;
procedure TMainFrm.Button1Click(Sender: TObject);
var
WordDoc:_Document;
begin
WordDoc:=OpenWordDoc(WordApp,"c:\. \My1.doc");
ReplaceWordText(WordDoc,"Привет","Пока",true);
// активизировать приложение
WordApp.Activate;
// максимизировать приложение
WordApp.WindowState:=wdWindowStateMaximize;
Мой Unit (писалось чисто для себя):
unit MyWordUnit;
interface
uses
// Word97;
Word2000,Variants;
// открывает документ и возвращает ссылку на него
function OpenWordDoc(WordApp:TWordApplication;S:string):_Document;
// сохранение документа в файл
procedure SaveWordDoc(WordDoc:_Document;S:string);
// поиск - замена текста
procedure ReplaceWordText(WordDoc:_Document;pFind,pRepl:string;All:boolean);
implementation
//-------------------------------
function OpenWordDoc(WordApp:TWordApplication;S:string):_Document;
var
FName:OleVariant;
begin
FName:=S;
Result:=WordApp.Documents.Open(FName,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam);
end;
//-------------------------------
procedure SaveWordDoc(WordDoc:_Document;S:string);
var
FName:OleVariant;
begin
FName:=S;
WordDoc.SaveAs(FName,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam);
end;
//-------------------------------
procedure ReplaceWordText(WordDoc:_Document;pFind,pRepl:string;All:boolean);
var
F,R,D:OleVariant;
begin
F:=pFind;
R:=pRepl;
if All then D:=wdReplaceAll else D:=wdReplaceOne;
WordDoc.Content.Find.Execute(F,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam,
R,
D,
EmptyParam,
EmptyParam,
EmptyParam,
EmptyParam);
end;
проверенно в 2000, XP, и 97 (здесь нужно будет только снести пару EmptyParam).
Читайте также: