MS Word VBA: Saving a document using the header - vba

I have been trying to figure out a way to, after performing a mail merge, separate the documents into individual ones and name them after a specific item, preferably the first line of the header. I have only been able to find ways to split the document, but cannot figure out how to name it. Any help with how to write the VBA code to save a document as the header would be very much appreciated.

Since you already separated the documents, the code below might give them names using their first sentence.
Private Function DocName(Doc As Document) As String
' 23 Aug 2017
Const Illegals As String = "\:/;?*|>"""
Static FaultCounter As Integer
Dim Fun As String
Dim Title As String
Dim Ch As String
Dim i As Integer
Title = Trim(Doc.Sentences(1))
For i = 1 To Len(Title)
Ch = Mid(Title, i, 1)
If (Asc(Ch) > 31) And (Asc(Ch) < 129) Then
If InStr(Illegals, Ch) = 0 Then Fun = Fun & Ch
End If
Next i
If Len(Fun) = 0 Then
FaultCounter = FaultCounter + 1
Fun = Format(FaultCounter, """Default File Name (""0"")""")
End If
DocName = Fun
End Function
Before saving the file you might want to check for duplicates. Use the Dir() function for that and add a number to duplicate names using the system I included above to name files where the first sentence might be empty.
You may also have to review the characters which aren't permitted in file names. I have simply excluded all below ASCII(32) and above ASCII(128), and then the known ones Windows doesn't like. You might want to modify that range further.
To call the above function use code like this:-
Private Sub GetName()
Debug.Print DocName(ActiveDocument)
End Sub

This is the code I have so far, I was able to find it off of a very helpful website, but the code saves as the word "report" which I set it to right now while I'm trying to figure it out, and then the number of the document.
Option Explicit
Sub splitter()
' splitter Macro
' Macro created by Doug Robbins to save each letter created by a mailmergeas
a separate file.
Application.ScreenUpdating = False
Dim Program As String
Dim DocName As String
Dim Letters As Integer, Counter As Integer
Letters = ActiveDocument.Sections.Count
Selection.HomeKey Unit:=wdStory
Counter = 1
While Counter < Letters
'program = ActiveDocument.MailMerge.DataSource.DataFields("Program_Outcomes_PlanReport_Name").Value
DocName = "Reports" & LTrim$(Str$(Counter)) 'Generic name of document
ActiveDocument.Sections.First.Range.Cut
Documents.Add
Selection.Paste
ActiveDocument.Sections(2).PageSetup.SectionStart = wdSectionContinuous
ActiveDocument.SaveAs filename:="E:\assessment rubrics\Templates" & "\" & DocName, FileFormat:=wdFormatDocument, LockComments:=False, Password:="",
AddToRecentFiles:=False, WritePassword:="", ReadOnlyRecommended:=False, EmbedTrueTypeFonts:=False, SaveNativePictureFormat:=False, SaveFormsData:=False, SaveAsAOCELetter:=False
ActiveWindow.Close
Counter = Counter + 1
Wend
Application.ScreenUpdating = True
End Sub

Related

get sentence with cursor and multiple commas in word vba

How do I get a sentence with multiple commas in MS Word with VBA that the cursor is in?
All the posts I've found said to get the sentence the cursor is in then use the code:
Selection.Sentences(1)
The above works well with a sentence with only 1 comma. But if I have a sentence with multiple commas like this:
For example, tomorrow is Tuesday(e.g., not Wednesday) or Thursday.
where the cursor is set somewhere in "For example" then "Selection.Sentences(1)" returns between the bars "...(e.g.|, |n...".
I'm using the latest version of Word. I plan on launching the code on an older version (I think 2013) that I first noticed the problem on.
This code is better suited to explain why MS didn't solve your problem than it is to actually solve it. However - depending upon your circumstances - you may like to play with it.
Option Explicit
Sub SelectSentence()
' 30 Jan 2018
' list abbreviations containing periods only
' in sequence of their expected frequency of occurrance
Const Abbs As String = "e.g.,f.i.,etc.,i.e."
Dim Fun As String ' sentence to select
Dim Para As Range
Dim SelStart As Long ' location of selection
Dim Sp() As String ' array of Abbs
Dim Cp() As String ' array of encoded Abbs
With Selection
Set Para = .Paragraphs(1).Range
SelStart = .Start
End With
Sp = Split(Abbs, ",")
With Para
Application.ScreenUpdating = False
.Text = CleanString(.Text, Sp, Cp)
Fun = ActiveDocument.Range(SelStart, SelStart + 1).Sentences(1).Text
SelStart = InStr(.Text, Fun) + .Start - 1
.Text = OriginalString(.Text, Cp)
.SetRange SelStart, SelStart + Len(Fun) - 1
Application.ScreenUpdating = True
.Select
End With
Fun = Selection.Text
Debug.Print Fun
End Sub
Private Function CleanString(ByVal Txt As String, _
Abbs() As String, _
Cp() As String) As String
' 30 Jan 2018
Dim i As Integer
ReDim Cp(UBound(Abbs))
For i = 0 To UBound(Abbs)
If InStr(Txt, ".") = 0 Then Exit For
Cp(i) = AbbToTxt(Abbs(i))
Txt = Replace(Txt, Abbs(i), Cp(i))
Next i
ReDim Preserve Cp(i)
CleanString = Txt
End Function
Private Function AbbToTxt(ByVal Abb As String) As String
' 30 Jan 2018
' use a character for Chr(92) not occurring in your document.
' Apparently it must be a character with a code below 128.
' use same character as function 'AbbToTxt'
AbbToTxt = Replace(Abb, ".", Chr(92))
End Function
Private Function OriginalString(ByVal Txt As String, _
Cp() As String) As String
' 30 Jan 2018
Dim i As Integer
For i = 0 To UBound(Cp) - 1
Txt = Replace(Txt, Cp(i), TxtToAbb(Cp(i)))
Next i
OriginalString = Txt
End Function
Private Function TxtToAbb(ByVal Txt As String) As String
' 30 Jan 2018
' use same character as function 'AbbToTxt'
TxtToAbb = Replace(Txt, Chr(92), ".")
End Function
For one, the code will only handle abbreviations which you program into it (see Const Abbs at the top of the code). For another, it will fail to recognise a period with dual meaning, such as "etc." found at the end of a sentence.
If you are allowed to edit the documents you work with, the better way of tackling your problem may well be to remove the offending periods with Find > Replace. After all, whoever understands "e.g." is also likely to understand "eg". Good Luck!

Concatenated string is not recognized by my Sub

I made the following Sub to help me copy values from other workbooks or even just from other sheets within the same workbook.
Private Sub CopyValues(fromSheet As String, fromRange As String, toSheet As String, toRange As String, Optional fromFileName As String = "")
Dim toFile As Excel.Workbook
Set toFile = ActiveWorkbook
Dim fromFile As Excel.Workbook
If Len(fromFileName) > 0 Then
Set fromFile = Workbooks.Open(fromFileName)
Else
Set fromFile = ActiveWorkbook
End If
With ActiveWorkbook
toFile.Sheets(toSheet).Range(toRange).Value = fromFile.Sheets(fromSheet).Range(fromRange).Value
End With
If Len(fromFileName) > 0 Then
fromFile.Close savechanges:=False
End If
End Sub
It works pretty well (and you all are free to use it if you find it helpful). Below is an example of code that works:
Call CopyValues(reportName, "B4:C15", reportName, "E2:F13", reportDirPath)
Unfortunately, I'm having trouble with a specific case. I'm looking to copy the same value into multiple cells in the same column. Below is what I came up with:
For i = 2 To i = 13
Call CopyValues(reportName, "AJ2", reportName, "H" + i, reportDirPath)
Next i
That didn't work. No error messages, but none of the values were pasted into my sheet. I thought that maybe concatenating the integer i was converting (is that the technical word?) the string to a different type, so I tried the following:
For i = 2 To i = 13
Call CopyValues(reportName, "AJ2", reportName, CStr("H" + i), reportDirPath)
Next i
That still didn't work. Same deal. No error messages, but none of the values were pasted into my sheet.
Changing the + to an & also didn't work:
For i = 2 To i = 13
Call CopyValues(reportName, "AJ2", reportName, CStr("H" & i), reportDirPath)
Next i
Obviously, I could just write out each individual case, but that seems kind of ridiculous. Any idea what's going on?
When I tried your code your 'For' loops were not working, but after I changed your for loop to say 'For i = 2 to 13' as opposed to 'For i=2 To i = 13' the last version of your code worked for me.
For i = 2 To 13
Call CopyValues("Sheet1", "A1", "Sheet2", CStr("J" & i))
Next i
End Sub
So I think that could have been your trouble.

Read from a web page and using two determiner for new row and next cell in vba excel

I am looking for a way to read from a feed webpage which its structure is something like
A,B,C;E,F,G;....
I want to read this data and put A B and C in the first row and put E F and G in row 2, and etc.
I was looking for a function in VBA, but most of them are for only one determiner.
I also was thinking of using string functions of VBA, which that would be the last resort! Since I must read a long string and then use a cursor (which I don't know if it is like c or not!) that probably leads to unstable performance because first I don't know the volume of data, and second I want to use it in a loop.
Could you please help me with the best solution?
feed = "A,B,C;E,F,G;...."
CSV = Replace( feed, ";", vbNewLine )
TSV = Replace( CSV , ",", vbTab )
Set do = CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}") ' this is a late bound MSForms.DataObject
do.SetText TSV
do.PutInClipboard
ActiveSheet.Paste
Sub Test()
ParseString1 "A,B,C;D,E,F;G,H,I,J,K,L"
ParseString2 "A,B,C;D,E,F;G,H,I,J,K,L"
End Sub
Sub ParseString1(data As String)
Dim clip As MSForms.DataObject
Set clip = New MSForms.DataObject
data = Replace(data, ",", vbTab)
data = Replace(data, ";", vbCrLf)
clip.SetText data
clip.PutInClipboard
Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial
End Sub
Sub ParseString2(data As String)
Dim aColumns, aRows
Dim x As Long
aRows = Split(data, ";")
For x = 0 To UBound(aRows)
aColumns = Split(aRows(x), ",")
Range("A" & Rows.Count).End(xlUp).Offset(1).Resize(1, UBound(aColumns) + 1) = aColumns
Next
End Sub
You'll need to set a reference to the Microsoft Forms 2.0 Object Library if you use ParseString1.

File name without extension name VBA

I need to get file name without extension name by VBA. I know ActiveWorkbook.Name property , but if user haves Windows property Hide extensions for known file types turn off, the result of my code will be [Name.Extension]. How can I return only name of Workbook independent of windows property?
I try even ActiveWorkbook.Application.Caption but I can't customize this property.
The answers given here already may work in limited situations, but are certainly not the best way to go about it. Don't reinvent the wheel. The File System Object in the Microsoft Scripting Runtime library already has a method to do exactly this. It's called GetBaseName. It handles periods in the file name as is.
Public Sub Test()
Dim fso As New Scripting.FileSystemObject
Debug.Print fso.GetBaseName(ActiveWorkbook.Name)
End Sub
Public Sub Test2()
Dim fso As New Scripting.FileSystemObject
Debug.Print fso.GetBaseName("MyFile.something.txt")
End Sub
Instructions for adding a reference to the Scripting Library
Simple but works well for me
FileName = ActiveWorkbook.Name
If InStr(FileName, ".") > 0 Then
FileName = Left(FileName, InStr(FileName, ".") - 1)
End If
Using the Split function seems more elegant than InStr and Left, in my opinion.
Private Sub CommandButton2_Click()
Dim ThisFileName As String
Dim BaseFileName As String
Dim FileNameArray() As String
ThisFileName = ThisWorkbook.Name
FileNameArray = Split(ThisFileName, ".")
BaseFileName = FileNameArray(0)
MsgBox "Base file name is " & BaseFileName
End Sub
This gets the file type as from the last character (so avoids the problem with dots in file names)
Function getFileType(fn As String) As String
''get last instance of "." (full stop) in a filename then returns the part of the filename starting at that dot to the end
Dim strIndex As Integer
Dim x As Integer
Dim myChar As String
strIndex = Len(fn)
For x = 1 To Len(fn)
myChar = Mid(fn, strIndex, 1)
If myChar = "." Then
Exit For
End If
strIndex = strIndex - 1
Next x
getFileType = UCase(Mid(fn, strIndex, Len(fn) - x + 1))
End Function
You could always use Replace() since you're performing this on the workbook's Name, which will almost certainly end with .xlsm by virtue of using VBA.
Using ActiveWorkbook per your example:
Replace(Application.ActiveWorkbook.Name, ".xlsm", "")
Using ThisWorkbook:
Replace(Application.ThisWorkbook.Name, ".xlsm", "")
This thread has been very helpful to me lately. Just to extend on the answer by #RubberDuck, the File System Object in the Microsoft Scripting Runtime library is already there to achieve this. Also if you define it as an Object as below, it will save you the hassle of having to enable 'Microsoft Scripting Runtime' in VBA Tools > References:
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Debug.Print fso.GetBaseName(ActiveWorkbook.Name)
In this way it will return name of the ActiveWorkbook without extension.
There is another way by using INSTRREV function as below:
Dim fname As String
fname = Left(ActiveWorkbook.Name, InStrRev(ActiveWorkbook.Name, ".") - 1)
MsgBox fname
Both will return the same result. Also in both of the methods above, they will retain any full-stops in the file name and only get rid of the last full-stop and the file extension.
To be verbose it the removal of extension is demonstrated for
workbooks.. which now have a variety of extensions .
. a new unsaved Book1 has no ext
. works the same for files
Function WorkbookIsOpen(FWNa$, Optional AnyExt As Boolean = False) As Boolean
Dim wWB As Workbook, WBNa$, PD%
FWNa = Trim(FWNa)
If FWNa <> "" Then
For Each wWB In Workbooks
WBNa = wWB.Name
If AnyExt Then
PD = InStr(WBNa, ".")
If PD > 0 Then WBNa = Left(WBNa, PD - 1)
PD = InStr(FWNa, ".")
If PD > 0 Then FWNa = Left(FWNa, PD - 1)
'
' the alternative of using split.. see commented out below
' looks neater but takes a bit longer then the pair of instr and left
' VBA does about 800,000 of these small splits/sec
' and about 20,000,000 Instr Lefts per sec
' of course if not checking for other extensions they do not matter
' and to any reasonable program
' THIS DISCUSSIONOF TIME TAKEN DOES NOT MATTER
' IN doing about doing 2000 of this routine per sec
' WBNa = Split(WBNa, ".")(0)
'FWNa = Split(FWNa, ".")(0)
End If
If WBNa = FWNa Then
WorkbookIsOpen = True
Exit Function
End If
Next wWB
End If
End Function
I use a macro from my personal.xlsb and run it on both xlsm and xlsx files so a variation on David Metcalfe's answer that I use is
Dim Wrkbook As String
Wrkbook = Replace(Application.ActiveWorkbook.Name, ".xlsx", ".pdf")
Wrkbook = Replace(Application.ActiveWorkbook.Name, ".xlsm", ".pdf")
Here is a solution if you do not want to use FSO.
There were some similar answers before, but here some checks are done to handle multiple dots in name and name without extension.
Function getFileNameWithoutExtension(FullFileName As String)
Dim a() As String
Dim ext_len As Integer, name_len As Integer
If InStr(FullFileName, ".") = 0 Then
getFileNameWithoutExtension = FullFileName
Exit Function
End If
a = Split(ActiveWorkbook.Name, ".")
ext_len = Len(a(UBound(a))) 'extension length (last element of array)
name_len = Len(FullFileName) - ext_len - 1 'length of name without extension and a dot before it
getFileNameWithoutExtension = Left(FullFileName, name_len)
End Function
Sub test1() 'testing the function
MsgBox (getFileNameWithoutExtension("test.xls.xlsx")) ' -> test.xls
MsgBox (getFileNameWithoutExtension("test")) ' -> test
MsgBox (getFileNameWithoutExtension("test.xlsx")) ' -> test
End Sub
strTestString = Left(ThisWorkbook.Name, (InStrRev(ThisWorkbook.Name, ".", -1, vbTextCompare) - 1))
full credit: http://mariaevert.dk/vba/?p=162

VBA code to delete files in a directory that contains specific characters

I need help in a VBA macro that'll delete files in a directory that contains more than 2 "_" and is older than 3 months old, however there are some folders & sub folders in the directory that must not be touched or modified.
E.g, Hi_Thanks_for_your_help or Hi_Thank_You etc.
Const DIR = "x"
Const MAX_AGE = 3 ' Unit: Months
Dim oFSO
Dim aExclude
Sub XLS()
aExclude = Array("x")
Set oFSO = CreateObject("Scripting.FilesystemObject")
deleteFiles oFSO.GetFolder(DIR)
Set oFSO = Nothing
End Sub
'=================================
Function isExclude(sPath)
Dim s, bAns
bAns = False
For Each s In aExclude
If InStr(1, sPath, s, vbTextCompare) = 1 Then
bAns = True
Exit For
End If
Next
isExclude = bAns
End Function
'=================================
Function isOldFile(fFile)
' Old file if "MAX_AGE" months before today is greater than the file modification time
isOldFile = (DateAdd("m", -MAX_AGE, Date) > fFile.DateLastModified)
End Function
This is the furthest i got with a code, what i'm lacking is how to check if a file name consists more than 2 "_" and if so & it's older than 3 months old = delete.
Thanks in advance! Cheers!
Dim pathname As String = ""
If fileNameCount("file_name") And DateDiff("m", NOW(), FileDateTime(pathname)) > 3 Then ' if '_' is more than 2 count and more than 3 months old, then delete
' if true delete file codes starts here
......
End If
Public Function fileNameCount(filename As String) As Boolean
fileNameCount = False
Dim count As Long
Dim temp() As String
temp = Split(filename, "_")
count = UBound(temp, 1)
If (count > 2) Then
fileNameCount = True
End If
End Function
I have written portion of the codes for you, the method fileNameCount will return you true / false for number of counts of '_', I'm using DateDiff to get the difference of the month of the file. Therefore I'm detecting on the both conditions, if both statement are true condition then you should proceed on with your deletion of file codes which I didn't write for that.
What you need to do is
1) Pass in the "file_name" argument which you need to think on how to get the file name
2) Pass in the right pathname of the file
3) Write the code for deletion of files
Anyway, I didn't test out the code so it might have some error(s). Hope this will help what you're trying to do.
To get the amount of "_" in a file, I would use something similar to this:
Dim a
Dim c As Integer
a = Split("File_Name_Here", "_")
c = Ubound(a)
Using this, you know that if the filename gets split into 3 or more substrings, there were 2 "_" in the filename. As for the age of the file, FileDateTime("FilePath") will get you the created date or the last modified date.