Word VBA Макросы: Как скопировать указанные страницы в другой файл?

Автор Maxx, 27 октября 2017, 21:43

Maxx

Доброго времени!
У меня задача такая: нужно скопировать текст с выбранных страниц в новый документ, а страницы нужны не все, а в произвольном порядке,  например: 2-5,6, 8, 22-35.
Прикладываю файл для теста.

Администратор

Вариант 1. Копируются сразу несколько страниц, указанные через дефис. В этой версии нет проверки правильности введённых данных в инпутбокс.
Вариант 2 находится ниже, в другом ответе.

Макрос копирует указанные страницы из активного файла (это файл, который отображается на мониторе) в новый пустой файл.

В процедуре "макрос", в пункте 6.5.a можно настроить, чтобы копируемые страницы выделялись перед копированием (иногда может потребоваться выделить), но обычно выделять не надо, чтобы копировать данные. Сейчас в макросе страницы не выделяются перед копированием.

Макрос
Sub макрос()
   
    Dim doc_act As Document, doc_new As Document
    Dim PagesNumbers, PageNumber1 As Long, PageNumber2 As Long
    Dim start_ As Long, end_ As Long
    Dim spl, i As Long
   
   
    '1. Юзер указывает номера страниц с помощью инпутбокса.
    PagesNumbers = InputBox("Укажите номера страниц. Пример: 1,3,5-7.")
    ' Если юзер щёлкнул "Cancel".
    If PagesNumbers = "" Then
        Exit Sub
    End If
    ' Разбивка номеров страниц по запятым, чтобы получился массив.
    PagesNumbers = Split(PagesNumbers, ",")
   
    '2. Отключение монитора (может это ускорит макрос и чтобы не мерцало).
    Application.ScreenUpdating = False
   
    '3. Присваиваем активному файлу (это файл, который отображается на мониторе) имя "doc_act".
        ' После создания нового файла, активным файлом станет новый файл, и уже нельля
        ' будет обратиться к исходному активному файлу, используя объект "ActiveDocument".
    Set doc_act = ActiveDocument
   
    '4. Проверка, что юзер указал существующие номера страниц.
    If VerifyPagesNumbers(doc_act, PagesNumbers) = False Then
        Exit Sub
    End If
   
    '5. Создание нового пустого файла и присваивание ему имени "doc_new".
    Set doc_new = Documents.Add
   
    '6. Копирование указанных страниц в новый файл.
        ' При использовании функции "Split" создаётся массив, в котором у первого элемента
        ' порядковый номер 0.
    For i = 0 To UBound(PagesNumbers)
       
        '1) Разбивка данных по дефису.
        spl = Split(PagesNumbers(i), "-")
       
        '2) Копирование номеров страниц из массива "spl" в две переменные.
        ' Если один элемент в массиве "spl", значит дефиса нет и нужно работать с одной страницей.
        If UBound(spl) = 0 Then
            ' Запись в переменные номеров страниц, с которыми надо работать.
            PageNumber1 = spl(0)
            PageNumber2 = spl(0)
        ' Если есть дефис, значит нужно работать с несколькими страницами.
        Else
            PageNumber1 = spl(0)
            PageNumber2 = spl(1)
        End If
       
        '3) Запись в переменную начала начальной страницы.
        start_ = doc_act.GoTo(What:=wdGoToPage, Which:=wdGoToAbsolute, Count:=PageNumber1).Start
       
        '4) Запись в переменную конца конечной страницы.
            ' Нужно в переменную записать начало следующей страницы после указанной конечной.
            ' Если конечная страница - это последняя страница, то запишем в переменную конец файла.
        If doc_act.ComputeStatistics(wdStatisticPages) = PageNumber2 Then
            end_ = doc_act.Range.End
        Else
            end_ = doc_act.GoTo(What:=wdGoToPage, Which:=wdGoToAbsolute, Count:=PageNumber2 + 1).Start
        End If
       

        '5) Копирование страниц в новый файл.
       
        'a) Копирование страниц, которые находятся в исходном файле.
        ' Нужный пункт раскомментируйте, а ненужный закомментируйте.
        ' Копирование без выделения.
        doc_act.Range(start_, end_).Copy
'        ' Выделение и копирование выделенного.
'        doc_act.Range(start_, end_).Select
'        doc_act.ActiveWindow.Selection.Copy
       
        'b) Вставка в конец нового файла знака абзаца, чтобы вставляемый текст встал снизу.
        ' Проверка, может быть пустой абзац уже есть. Такое может быть например,
            ' если ворд-файл пустой, если в конце есть разрыв страниц, и наверное и в других случаях.
        If doc_new.Paragraphs.Last.Range.text <> Chr(13) Then
            doc_new.Range.InsertParagraphAfter
        End If
       
        'c) Вставка скопированных данных в конец нового файла.
            ' doc_new.Range.End - 1, doc_new.Range.End - 1) - это позиция перед самым последним знаком абзаца.
            ' .PasteAndFormat wdFormatOriginalFormatting - вставка исходного форматирования (то есть
                ' форматирования, которое имеет текст в исходном файле).
        doc_new.Range(doc_new.Range.End - 1, doc_new.Range.End - 1).PasteAndFormat wdFormatOriginalFormatting
       
    Next i
   
    '7. Очистка буфера обмена, чтобы при закрытии программы "Word" не было сообщения,
        ' что в буфере обмена содержится много данных. Для этого просто копируем любой один символ.
    doc_act.Characters(1).Copy
   
    '8. Включение монитора.
    Application.ScreenUpdating = True
   
    '9. Сообщение.
    MsgBox "Готово.", vbInformation
   
End Sub

Private Function VerifyPagesNumbers(doc_act As Document, PagesNumbers) As Boolean

    ' Проверка, что юзер указал существующие номера страниц.
   
    Dim PageNumber1 As Long, PageNumber2 As Long
    Dim spl, i As Long


    For i = 0 To UBound(PagesNumbers)
       
        '1. Разбивка данных по дефису.
        spl = Split(PagesNumbers(i), "-")
       
        '2. Копирование номеров страниц из массива "spl" в две переменные.
        ' Если один элемент в массиве "spl", значит дефиса нет и нужно работать с одной страницей.
            ' При использовании функции "Split" создаётся массив, в котором у первого элемента
            ' порядковый номер 0.
        If UBound(spl) = 0 Then
            ' Запись в переменные номеров страниц, с которыми надо работать.
            PageNumber1 = spl(0)
            PageNumber2 = spl(0)
        ' Если есть дефис, значит нужно работать с несколькими страницами.
        Else
            PageNumber1 = spl(0)
            PageNumber2 = spl(1)
        End If
       
        '3. Проверка, что указанная начальная страница существует.
        If doc_act.ComputeStatistics(wdStatisticPages) < PageNumber1 Then
            Application.ScreenUpdating = True
            MsgBox "В файле нет страницы: " & PageNumber1, vbExclamation
            Exit Function
        End If
       
        '4. Проверка, что указанная конечная страница существует.
        If doc_act.ComputeStatistics(wdStatisticPages) < PageNumber2 Then
            Application.ScreenUpdating = True
            MsgBox "В файле нет страницы: " & PageNumber2, vbExclamation
            Exit Function
        End If
       
    Next i
       
    '5. Запись в переменную-функцию "VerifyPagesNumbers" слова True.
    VerifyPagesNumbers = True
       
End Function
[свернуть]

Если скопированный текст имеет неправильное оформление, то попробуйте различные способы вставки в процедуре "макрос", в пункте 6.5.c. Например, можно использовать такой способ вставки:

        'c) Вставка скопированных данных в конец нового файла в html-формате.
            ' doc_new.Range.End - 1, doc_new.Range.End - 1) - это позиция перед самым последним знаком абзаца.
        doc_new.Range(doc_new.Range.End - 1, doc_new.Range.End - 1).PasteSpecial _
            Link:=False, DataType:=wdPasteHTML, Placement:=wdInLine, DisplayAsIcon:=False

Maxx

Большое спасибо! Но отступ первой строки у меня пропадает при копировании.
В коде макроса, в пункте 6.5. пробовал различные варианты, но не получилось.
Попробуйте скопировать страница 1 - 4.

Администратор

Если копировать в самом ворде (без макроса), то тоже не получается сделать так, как надо. Здесь уже брак в самом ворде - недостаточно просто скопировать и вставить.
С чем связана проблема, не знаю.

Администратор

Вариант 2. В этом варианте всегда копируется по одной странице, даже если юзер указал в инпутбоксе страницы через дефис.
В этом варианте делается проверка правильности данных, которые ввёл юзер в инпутбокс.
Вариант 1 находится выше, в другом ответе.

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

Макрос
Sub макрос()
   
    Dim doc_act As Document, doc_new As Document, PagesNumbers
    Dim start_ As Long, end_ As Long
    Dim spl, i As Long, ii As Long
   
   
    '1. Присваиваем активному файлу (это файл, который отображается на мониторе) имя "doc_act".
        ' После создания нового файла, активным файлом станет новый файл, и уже нельля
        ' будет обратиться к исходному активному файлу, используя объект "ActiveDocument".
    Set doc_act = ActiveDocument
   
    '2. Юзер указывает номера страниц с помощью инпутбокса.
    Do
   
        ' Defaul  - если юзер укажет неправильные данные, чтобы в инпутбоксе были
            ' отображены ранее введённые данные.
        PagesNumbers = InputBox(Prompt:="Укажите номера страниц. Пример: 1,3,5-7.", Default:=PagesNumbers)
        ' Если юзер щёлкнул "Cancel".
        If PagesNumbers = "" Then
            Exit Sub
        End If
        ' Разбивка номеров страниц по запятым, чтобы получился массив.
        PagesNumbers = Split(PagesNumbers, ",")
       
        ' Проверка, что юзер правильно записал номера страниц.
        If VerifyInputData(PagesNumbers) = False Then
            GoTo metka_NextInput
        End If
       
        ' Проверка, что юзер указал существующие номера страниц.
        If VerifyPagesNumbers(doc_act, PagesNumbers) = True Then
            Exit Do
        End If
       
metka_NextInput:
        ' Объединение в одну строку, чтобы подставить в инпутбокс.
        PagesNumbers = Join(PagesNumbers, ",")
       
    Loop
   
    '3. Отключение монитора (может это ускорит макрос и чтобы не мерцало).
    Application.ScreenUpdating = False
   
    '4. Создание нового пустого файла и присваивание ему имени "doc_new".
    Set doc_new = Documents.Add
   
    '5. Копирование указанных страниц в новый файл.
        ' При использовании функции "Split" создаётся массив, в котором у первого элемента
        ' порядковый номер 0.
    For i = 0 To UBound(PagesNumbers)
       
        '1) Разбивка данных по дефису.
        spl = Split(PagesNumbers(i), "-")
       
        '2) Вставка в конец нового файла знака абзаца, чтобы вставляемый текст встал снизу.
        ' Проверка, может быть пустой абзац уже есть. Такое может быть например,
            ' если ворд-файл пустой, если в конце есть разрыв страниц, и наверное и в других случаях.
        If doc_new.Paragraphs.Last.Range.text <> Chr(13) Then
            doc_new.Range.InsertParagraphAfter
        End If
           
        For ii = spl(0) To spl(UBound(spl))
       
            '3) Запись в переменную начала страницы.
            start_ = doc_act.GoTo(What:=wdGoToPage, Which:=wdGoToAbsolute, Count:=ii).Start
           
            '4) Запись в переменную конца страницы.
                ' Нужно в переменную записать начало следующей страницы после указанной.
                ' Если указанная страница - это последняя страница, то запишем в переменную конец файла.
            If doc_act.ComputeStatistics(wdStatisticPages) = ii Then
                end_ = doc_act.Range.End
            Else
                end_ = doc_act.GoTo(What:=wdGoToPage, Which:=wdGoToAbsolute, Count:=ii + 1).Start
            End If
           
   
            '5) Копирование страниц в новый файл.
           
            'a) Копирование страницы, которая находится в исходном файле.
            ' Нужный пункт раскомментируйте, а ненужный закомментируйте.
            ' Копирование без выделения.
            doc_act.Range(start_, end_).Copy
    '        ' Выделение и копирование выделенного.
    '        doc_act.Range(start_, end_).Select
    '        doc_act.ActiveWindow.Selection.Copy
           
            'c) Вставка скопированных данных в конец нового файла.
                ' doc_new.Range.End - 1, doc_new.Range.End - 1) - это позиция перед самым последним знаком абзаца.
                ' .PasteAndFormat wdFormatOriginalFormatting - вставка исходного форматирования (то есть
                    ' форматирования, которое имеет текст в исходном файле).
            doc_new.Range(doc_new.Range.End - 1, doc_new.Range.End - 1).PasteAndFormat wdFormatOriginalFormatting
       
        Next ii
       
    Next i
   
    '6. Очистка буфера обмена, чтобы при закрытии программы "Word" не было сообщения,
        ' что в буфере обмена содержится много данных. Для этого просто копируем любой один символ.
    doc_act.Characters(1).Copy
   
    '7. Включение монитора.
    Application.ScreenUpdating = True
   
    '8. Сообщение.
    MsgBox "Готово.", vbInformation
   
End Sub

Private Function VerifyInputData(PagesNumbers) As Boolean

    ' Проверка, что юзер правильно записал номера страниц.
        ' Проверка, что кроме запятой, дефиса и цифр нет других символов.
        ' Пробелы тоже могут быть, но лучше их не надо указывать.
   
    Dim spl, i As Long, ii As Long
   
    For i = 0 To UBound(PagesNumbers)
        ' Разбивка по дефису.
        spl = Split(PagesNumbers(i), "-")
        For ii = 0 To UBound(spl)
            ' Если это не число, то выход из функции.
            If IsNumeric(spl(ii)) = False Then
                Application.ScreenUpdating = True
                MsgBox "Введены неправильные данные.", vbExclamation
                Exit Function
            End If
        Next ii
    Next i
   
    ' Запись в переменную-функцию "VerifyInputData" слова "True".
    VerifyInputData = True
   
End Function

Private Function VerifyPagesNumbers(doc_act As Document, PagesNumbers) As Boolean

    ' Проверка, что юзер указал существующие номера страниц.
   
    Dim PageNumber1 As Long, PageNumber2 As Long
    Dim spl, i As Long


    For i = 0 To UBound(PagesNumbers)
       
        '1. Разбивка данных по дефису.
        spl = Split(PagesNumbers(i), "-")
       
        '2. Копирование номеров страниц из массива "spl" в две переменные.
        ' Если один элемент в массиве "spl", значит дефиса нет и нужно работать с одной страницей.
            ' При использовании функции "Split" создаётся массив, в котором у первого элемента
            ' порядковый номер 0.
        If UBound(spl) = 0 Then
            ' Запись в переменные номеров страниц, с которыми надо работать.
            PageNumber1 = spl(0)
            PageNumber2 = spl(0)
        ' Если есть дефис, значит нужно работать с несколькими страницами.
        Else
            PageNumber1 = spl(0)
            PageNumber2 = spl(1)
        End If
       
        '3. Проверка, что указанная начальная страница существует.
        If doc_act.ComputeStatistics(wdStatisticPages) < PageNumber1 Then
            Application.ScreenUpdating = True
            MsgBox "В файле нет страницы: " & PageNumber1, vbExclamation
            Exit Function
        End If
       
        '4. Проверка, что указанная конечная страница существует.
        If doc_act.ComputeStatistics(wdStatisticPages) < PageNumber2 Then
            Application.ScreenUpdating = True
            MsgBox "В файле нет страницы: " & PageNumber2, vbExclamation
            Exit Function
        End If
       
    Next i
       
    '5. Запись в переменную-функцию "VerifyPagesNumbers" слова True.
    VerifyPagesNumbers = True
       
End Function
[свернуть]

Maxx