VBA Word 拆分并使用指定名称保存
Posted
技术标签:
【中文标题】VBA Word 拆分并使用指定名称保存【英文标题】:VBA Word Split and Save with Speficied Name 【发布时间】:2019-09-09 20:23:28 【问题描述】:我希望这里有人可以帮助我。我有一份包含 365 封求职信的文档,我需要将其拆分为单独的文档,并将它们与地址块中的名称一起保存。有人可以帮我修改这段代码吗?以为我弄明白了,但我仍然遇到错误。
这是我尝试过但无法正常工作的代码。
Sub SplitIntoPages()
Dim docMultiple As Document
Dim docSingle As Document
Dim rngPage As Range
Dim iCurrentPage As Integer
Dim iPageCount As Integer
Dim strNewFileName As String
Application.ScreenUpdating = False 'Makes the code run faster and reduces screen _
flicker a bit.
Set docMultiple = ActiveDocument 'Work on the active document _
'(the one currently containing the Selection)
Set rngPage = docMultiple.Range 'instantiate the range object
iCurrentPage = 1
'get the document's page count
iPageCount = docMultiple.Content.ComputeStatistics(wdStatisticPages)
Do Until iCurrentPage > iPageCount
If iCurrentPage = iPageCount Then
rngPage.End = ActiveDocument.Range.End 'last page (there won't be a next page)
Else
'Find the beginning of the next page
'Must use the Selection object. The Range.Goto method will not work on a page
Selection.GoTo wdGoToPage, wdGoToAbsolute, iCurrentPage + 1
'Set the end of the range to the point between the pages
rngPage.End = Selection.Start
End If
rngPage.Copy 'copy the page into the Windows clipboard
Set docSingle = Documents.Add 'create a new document
docSingle.Range.Paste 'paste the clipboard contents to the new document
'remove any manual page break to prevent a second blank
docSingle.Range.Find.Execute Findtext:="^m", ReplaceWith:=""
'build a new sequentially-numbered file name based on the original multi-paged file name and path
Set objFileName = objNewDoc.Range(Start:=10, End:=30 & ".doc") 'docSingle.SaveAs objNewDoc.Range(Start:=10, End:=30 & ".doc") 'save the new single-paged document
iCurrentPage = iCurrentPage + 1 'move to the next page
docSingle.Close 'close the new document
rngPage.Collapse wdCollapseEnd 'go to the next page
Loop 'go to the top of the do loop
Application.ScreenUpdating = True 'restore the screen updating
'Destroy the objects.
Set docMultiple = Nothing
Set docSingle = Nothing
Set rngPage = Nothing
End Sub
这是我使用的原始代码,但将单个文档命名为原始文档的名称,并在末尾添加页码。
我希望得到一个代码,它将文件命名为地址块中的名称。任何帮助都可以提前谢谢你。
【问题讨论】:
这就是 Word 的邮件合并功能的设计目的。我不清楚你为什么要重新发明这个。 support.microsoft.com/en-us/help/318118/… 它合并成一个文件,我现在正在尝试将它们分开。 我认为有几种方法可以解决您的问题。 (1) 获取从代码中获取的文件并重命名生成的文件。您是否有地址块 = 收件人 = 文件名的列表?那么这只是一个简单的“重命名文件问题”! (2) 结果是pdf文件可能吗?每封求职信都是一页吗?如果两者都是:将大文件转换为 pdf 并将 pdf 拆分为单页文件。然后再次重命名文件。 你是从邮件合并/连载信开始的吗?这是一个解决方案吗?见***.com/questions/12594828/… 而代码应该如何知道“地址块”是什么? 【参考方案1】:@JenniferVazquez 你的代码有很多问题。
-
您有许多未声明的变量。这可能是因为您没有将“选项显式”作为模块的第一行。始终始终将选项显式放在您编写的任何模块或类的第一行。
然后总是在尝试运行代码之前执行 Debug.Compile。
除了 'Option Explicit' 和 Debug.COmpile,如果您的公司允许,请安装神奇的 RubberDuck 插件并密切注意此插件可以提供的代码检查。
您实际上还没有为新文件命名。事实上,您的代码不太可能像上面给出的那样运行。
您真的应该提供一个示例文档,我们可以根据该文档检查您的代码,看看我们是否得到与您相同的结果,并在我们编写新代码或更新您的代码时为我们提供帮助。
我已经编写了一些代码,我认为它们可以完成您的原始代码正在尝试做的事情。在我的代码中,我将活动拆分为不同的功能。如果我更加努力,我可以将我的代码拆分成更小的函数,但我想你会看到大致的想法。
感谢您在您的代码中添加了大量 cmets,它确实让您更容易弄清楚您想要做什么。
希望下面的代码对你有所帮助。
Option Explicit
Public Sub Test()
SplitIntoIndividualLetters ActiveDocument
End Sub
Public Sub SplitIntoIndividualLetters(Optional ByRef ipDocument As Word.Document = Nothing)
Dim myCurrentLetterRange As Word.Range
Dim myClientName As String
Set myCurrentLetterRange = GetNextLetterRange(IIf(ipDocument Is Nothing, ActiveDocument, ipDocument))
Do While Not myCurrentLetterRange Is Nothing
myClientName = GetClientname(myCurrentLetterRange.Duplicate)
If Not TrySaveIndividualLetter(myCurrentLetterRange.Duplicate, myClientName) Then
MsgBox "Something went wrong, the letter for " & myClientName & " was not saved", vbOKOnly
Stop
End If
Set myCurrentLetterRange = GetNextLetterRange(IIf(ipDocument Is Nothing, ActiveDocument, ipDocument))
Loop
End Sub
Private Function GetNextLetterRange(ByRef ipDocument As Word.Document) As Word.Range
' The use of Static means that the vairable will be remembered between calls
' so we don't need a module or global level variable to remeber it for us.
'
' On the first method call the variable myLetterRange will be 'nothing' as it won't
' yet have been initialised.
' This code uses the assumption that the individual letters are separated by
' a manual page break. In a word document this is the equivalent of a character
' with the code of 12
Static myLetterRange As Word.Range
' There are two special cases we need to deal with
'1. the first use of this function
'2. the end of the document
' On first use myLetterRange will not have been initialised so will be nothing
If myLetterRange Is Nothing Then
Set myLetterRange = ipDocument.StoryRanges(wdMainTextStory)
' Lets start at the beginning
myLetterRange.Collapse direction:=wdCollapseStart
' If we have reached the end of the document then we return nothing
ElseIf myLetterRange.End = ipDocument.Range.End Then
Set myLetterRange = Nothing
' In this case we can go home early
Exit Function
' If it not the start or the end of the document then we need to skip over the
' manual page break to get to the first character of the next letter
Else
myLetterRange.Collapse direction:=wdCollapseEnd
myLetterRange.Move unit:=wdCharacter, Count:=1
End If
' Now we can look for the manual page break that marks the end of the letter
' Moveenduntil will return the number of characters moved but will
' return 0 if we don't find any characters in cset
' This will happen at the last page of the document so to be able to return
' the range of the last page of the document we need to set the end of
' myLetterRange manually
If myLetterRange.MoveEndUntil(cset:=Chr$(12), Count:=wdForward) = 0 Then
myLetterRange.End = ipDocument.StoryRanges(wdMainTextStory).End
End If
'We don't want the user to corrupt our range so we return a copy
Set GetNextLetterRange = myLetterRange.Duplicate
End Function
Private Function GetClientname(ByRef ipLetterRange As Word.Range) As String
' The problem we have here is that the only clue we have as to the address block is
' that the 'Client' name lives in characters 10 to 30 of the letter range
' For the purposes of this code we'll assume that characters
' 10 to 30 live in paragraph 1 of the document.
' if this isn't the case you'll need to change the pragraph number and possible
' the numbers describing the start and end of the range
Dim myNameRange As Word.Range
Set myNameRange = ipLetterRange.Paragraphs(1).Range.Characters.First 'alternative is .characters(1)
myNameRange.MoveStart unit:=wdCharacter, Count:=10
' In this case the move also moves the end of the range
myNameRange.MoveEnd unit:=wdCharacter, Count:=20
GetClientname = myNameRange.Text
End Function
Private Function TrySaveIndividualLetter(ByRef ipLetterRange As Word.Range, ByVal ipClientName As String) As Boolean
Dim myLetter As Word.Document
Dim myLetterName As String
Set myLetter = Application.Documents.Add(Visible:=False)
' We give a name to the new letter as being the parentlettername_clientname
' delete the bits you don't want
' Use the docX extension that matches your multiple letter document
' in the line below
myLetterName = _
ipLetterRange.Document.Path _
& "\" _
& Replace(ipLetterRange.Document.Name, ".docm", vbNullString) _
& "_" _
& ipClientName _
& ".docx"
' Copy the formatted text in the found letter range into the new document
' copy/paste mioght be a better apprach if the range contains graphics.
myLetter.Range.FormattedText = ipLetterRange.FormattedText
myLetter.SaveAs2 myLetterName
TrySaveIndividualLetter = myLetter.Saved
myLetter.Close
结束函数
【讨论】:
以上是关于VBA Word 拆分并使用指定名称保存的主要内容,如果未能解决你的问题,请参考以下文章
在 VBA 中将 Word 对象分配给 Variant 变量
为啥每次使用 VBA 保存 word 文档时文件大小都会增加?