Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: MsWORD Automation  (Прочитано 16541 раз)
0 Пользователей и 1 Гость смотрят эту тему.
sergvg
Интересующийся

ru
Offline Offline

« : 12-05-2010 10:54 » 

Здравствуйте, уважаемые коллеги!

Помогите, пожалуйста!

подскажите, каким образом можно найти в документе Word все слова, перед которыми стоит специальный знак (например, '*') и записать их в массив, затем все полученные слова (вместе с предшествующим знаком) заменить на другой текст?

пока пришло такое решение (поиск меток):
Код:
$WordsCount = $File->Words->{Count};
my $pword;
for $i (1..$WordsCount){
  $word = $File->Words($i)->{Text};
  if ($pword eq '*' ) {
    push @$words ,$word;
  }
  $pword = $word;
}

и затем произвести замену с помощью этого
Код:
my $search =  $doc-> Content->Find;
my $replace = $search-> Replacement;

$search-> {Text} = $oldtext;
$replace-> {Text} = $newtext;
$search-> Execute({Replace => wdReplaceAll});


Но я думаю, что есть более эффективный метод.


Метод с закладками (bookmarks) мне не нравится, потому что неудобно клепать шаблоны. Намного нагляднее в тексте шаблона видеть метки, поэтому этот метод предлагать не нужно (уже есть у меня готовое решение с использованием закладок).

Добавлено через 1 час, 45 минут и 39 секунд:
В общем, у меня получился такой код для поиска и замены меток вида *Name

Код:
my $Word = CreateObject Win32::OLE 'Word.Application' or die $!;
$Word->{'Visible'} = 1;


my $File = $Word->Documents->Open(abs_path($ARGV[0]));

#$ActiveDoc = $Word->ActiveDocument;

my $replaces={'PrikazNum'=>'Номер приказа 222', 'Date'=>'Дата: 2010-01-02', 'sign'=>'Подпись: 2222'};

my $myRange = $File->Content;
while ($myRange->{Find}->Execute ({FindText=>'*', Forward=>1}) ){
    $foundrange=$myRange->Next({Unit=>wdWord, Count=>1});
    $repltext = $replaces->{$foundrange->{Text}};
    $foundrange->MoveStart({Unit=>wdCharacter, Count=>-1});
    print "'$foundrange->{Text}' заменяем на '$repltext'\n";
    $foundrange->{Text} = $repltext;
}

Вопрос остался такой: как избежать появления пробелов в $foundrange->{Text} ? Сюда пробел попадает, другие символы нет (если в документе встречаются).
« Последнее редактирование: 12-05-2010 12:40 от sergvg » Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #1 : 13-05-2010 05:52 » 

sergvg, а регулярные выражения ты не пробовал? Судя по условию задачи - это самый подходящий вариант для тебя
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 13-05-2010 06:10 » 

Чего-то я не пойму: листинги на Perl, а раздел - VBA.

Замеченные недостатки - нет проверки на наличие ключа:

Код:
$repltext = $replaces->{$foundrange->{Text}};


Наверно стоит так:

Код:
    $foundrange->{'Text'} ~= m/^(\s*)(.*?)(\s*)$/;

    if (exists($replaces->{$2})
    {
        $foundrange->{'Text'} = $1 . $replaces->{$2} . $3;
    }
« Последнее редактирование: 13-05-2010 06:34 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
sergvg
Интересующийся

ru
Offline Offline

« Ответ #3 : 13-05-2010 07:08 » 

У меня вопросы не по Perl, а по VBA (перл я знаю отлично, а вот с VBA не работал совсем)!


Какие объекты использовать, и какие методы вызывать!

И в моём случае, проверку на наличие ключей хеша можно не делать, потому что документ (шаблон) сначала загружается на сервер, скриптом получается список меток. А затем, другим скриптом эти метки заполняются, где формируется хеш по списку параметров.

Прошу перенести тему обратно в VBA.


PS: о каких регулярных выражениях шла речь ?  в VBA есть регеспы ?


со стороны Perl эти пробелы в конце, разумеется, я поубирал.
« Последнее редактирование: 13-05-2010 07:27 от RXL » Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 13-05-2010 07:34 » 

Понятие "знаю отлично" у каждого свое. На мой взгляд для этого не хватает, для начала, use strict и вытекающих из этого ограничений на необъявленные переменные и голые слова, а также проверок, т.к. со strict все огрехи по вылезают как ошибки, причем, ошибка с отсутствующим ключем может вылезти уже в процессе эксплуатации. Т.ч. посторожнее с утверждениями...

К VBA вопрос отношения не имеет. Это работа с COM-объектами. То, что в VB и VBA все объекты являются COM-объектами, еще не ставит между ними знака равенства. Точно также можно работать с Вордом на любом другом языке. Я, к примеру, использую экспорт в Excel из Borland C++ Builder. Значит темы BCB тоже в VBA переносить?

Что и как у тебя формируется — мы не видим и, соответственно, знать не можем. Судим по приведенному коду и заданным вопросам.
« Последнее редактирование: 13-05-2010 07:36 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
sergvg
Интересующийся

ru
Offline Offline

« Ответ #5 : 13-05-2010 09:21 » 

Если мне нужно уточнить, узнать какие объекты, их свойства  и методы использовать для того, чтобы поуправлять MS Word'ом, то причём тут Perl ?

а куски кода на Perl, потому что я пишу на Perl, и слабо знаю синтаксис VBA, чтобы привести к нему.
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #6 : 13-05-2010 10:58 » 

sergvg, если ты спрашиваешь про объектную модель Word, то так и говори.
Мы видим конкретный вопрос:
Вопрос остался такой: как избежать появления пробелов в $foundrange->{Text} ? Сюда пробел попадает, другие символы нет (если в документе встречаются).

И отвечаем, что лучше уж использовать регулярные выражения, и RXL даже пример привел.
Никто не говорил ни слова про VBA. Поскольку пример на Perl, то и очевидно что ожидается решение на Perl.
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 13-05-2010 12:57 » 

sergvg, если не знаешь, как-то делай просто: открывай документ, запускай запись макроса и выполняй действия руками (старайся делать минимум  действий - только нужные), после чего в макросе видим последовательность действий с объектами. Код там не сложный - на Perl портируется чуть ли не один в один. Собственно говоря, у тебя похоже так и сделано. У меня получилось немного иначе:

Код:
    Selection.Find.Execute
    Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend

Но суть та же: если после "*слово" есть пробел, то он тоже выделяется. Думаю, что не надо ничего делать - бери, что дают, реж регулярными выражениями (в Перле!) и делай с ним что надо.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 13-05-2010 13:29 » 

не знаю, есть ли регулярки в VBA, но в VB есть объект RegExp

Dim myRegExp As New RegExp
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #9 : 13-05-2010 13:43 » 

Слав, VBA тут не при чем - нету его. Есть Perl и COM-объект Word.Application.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
sergvg
Интересующийся

ru
Offline Offline

« Ответ #10 : 13-05-2010 17:33 » 

sergvg, если не знаешь, как-то делай просто: открывай документ, запускай запись макроса и выполняй действия руками (старайся делать минимум  действий - только нужные), после чего в макросе видим последовательность действий с объектами. Код там не сложный - на Perl портируется чуть ли не один в один. Собственно говоря, у тебя похоже так и сделано. У меня получилось немного иначе:

Код:
    Selection.Find.Execute
    Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend

Но суть та же: если после "*слово" есть пробел, то он тоже выделяется. Думаю, что не надо ничего делать - бери, что дают, реж регулярными выражениями (в Перле!) и делай с ним что надо.

так-то понятно, но я долго искал в хелпе, как получить в перле уже список отмеченных слов, ещё неясно было, как продолжить поиск (после изменения Range поиск зацикливался, начинаясь наверно или сначала, или поиск производился в измененном Range)

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

Было бы удобнее, если бы был поиск с регеспами, искал бы по /\*.(\w+)/. Или может есть, а я не знаю?
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #11 : 13-05-2010 19:49 » 

Полноценных регулярных выражений в Ворде нет, но можно делать примитивные шаблоны типа как в оболочке с именами файлов. Собственно, это описано в MSDN и даже в самом Ворде, когда открываешь поиск и делаешь его расширенным (и включи галку "Подстановочные знаки", а символы можно выбрать из выпадающего списка).

Find object members: http://msdn.microsoft.com/en-us/library/bb257583.aspx
Посмотри свойства: IgnoreSpace, MatchWildcards.

Простые эксперименты руками в Ворде дали такой шаблон: \*<*>
Это выбирает звездочку и слово за ним.

Объекты Ворда: http://msdn.microsoft.com/en-us/library/bb225019.aspx


Добавлено через 29 минут и 15 секунд:
Вот макрос Ворда.

Код:
    Selection.Find.ClearFormatting
    Selection.Find.Replacement.ClearFormatting
    With Selection.Find
        .Text = "\*<*>"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = True
    End With
    Selection.Find.Execute
    With Selection.Find
        .Text = "\*<*>"
        .Replacement.Text = "1111"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = True
    End With
    With Selection
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseStart
        Else
            .Collapse Direction:=wdCollapseEnd
        End If
        .Find.Execute Replace:=wdReplaceOne
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseEnd
        Else
            .Collapse Direction:=wdCollapseStart
        End If
        .Find.Execute
    End With
    With Selection.Find
        .Text = "\*<*>"
        .Replacement.Text = "2222"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = True
    End With
    With Selection
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseStart
        Else
            .Collapse Direction:=wdCollapseEnd
        End If
        .Find.Execute Replace:=wdReplaceOne
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseEnd
        Else
            .Collapse Direction:=wdCollapseStart
        End If
        .Find.Execute
    End With
    With Selection.Find
        .Text = "\*<*>"
        .Replacement.Text = "3333"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = True
    End With
    With Selection
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseStart
        Else
            .Collapse Direction:=wdCollapseEnd
        End If
        .Find.Execute Replace:=wdReplaceOne
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseEnd
        Else
            .Collapse Direction:=wdCollapseStart
        End If
        .Find.Execute
    End With

Если его подчистить, то получится примерно следующее:

Инициализация
Код:
    Selection.Find.ClearFormatting
    Selection.Find.Replacement.ClearFormatting

    With Selection.Find
        .Text = "\*<*>"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = True
    End With

Поиск
Код:
    Selection.Find.Execute

Замена
Код:
    Selection.Find.Replacement.Text = "1111"

    With Selection
        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseStart
        Else
            .Collapse Direction:=wdCollapseEnd
        End If

        .Find.Execute Replace:=wdReplaceOne

        If .Find.Forward = True Then
            .Collapse Direction:=wdCollapseEnd
        Else
            .Collapse Direction:=wdCollapseStart
        End If
    End With
« Последнее редактирование: 13-05-2010 20:23 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
sergvg
Интересующийся

ru
Offline Offline

« Ответ #12 : 14-05-2010 03:17 » new

спасибо, на досуге опробую.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines