In MSAccess VBA, I'm trying to parse a name field into last, first, middle. The problem is that the incoming format is not consistent:
Jones John Q
Doe Jane
Smith Robert X
This is what I'm doing
Dim rsNames As DAO.Recordset
Set rsNames = CurrentDb.OpenRecordset("SELECT * FROM tblInput")
If Not (rsNames.EOF And rsNames.BOF) Then
rsNames.MoveFirst
Do Until rsNames.EOF = True
strFullName = rsNames!Name
intLength = Len(strFullName)
intSpacePos = InStr(strFullName, " ")
strLname = Left(strFullName, intSpacePos - 1)
strFname = Mid(strFullName, intSpacePos, intLength - (intSpacePos - 1))
strFname = Trim(strFname)
If Len(strFname) + Len(strLname) + (intSpacePos - 1) < intLength Then
strMI = Right(strFullName, 1)
End If
rsNames.Edit
rsNames!LastName = strLname
rsNames!FirstName = strFname
rsNames!MiddleInitial = strMI
rsNames.Update
rsNames.MoveNext
Loop
Results
LastName: Jones
FirstName: John Q
Middle Initial: Q
LastName: Doe
FirstName: Jane
Middle Initial: E
If I change this line
strFname = Mid(strFullName, intSpacePos, intLength - (intSpacePos - 1)) to
strFname = Mid(strFullName, intSpacePos, intLength - (intSpacePos), the lines with middle initials parse correctly, but the lines without middle initials cut off the last character of the first name and move it to the middle initial field (Doe Jan E)
I've tried using split and replace but neither works properly because of the varying numbers of spaces separating the fields. I'm wondering if my only option is to read the string character by character and building the individual fields that way, but before I go down that path, am I missing something obvious? I have no control over the incoming file.
I'll propose you to use split() function, in this manner:
Dim rsNames As DAO.Recordset
Dim strLname As String, strFname As String, strMI As String
Dim i As Integer
Dim x, arr As Variant
Set rsNames = CurrentDb.OpenRecordset("SELECT * FROM tblInput")
If Not (rsNames.EOF And rsNames.BOF) Then
'rsNames.MoveFirst
Do Until rsNames.EOF = True
arr = Split(rsNames!Name)
strLname = ""
strFname = ""
strMI = ""
i = 0
For Each x In arr
If (x <> "") Then
If (i = 0) Then
strLname = x
ElseIf (i = 1) Then
strFname = x
Else
strMI = x
End If
i = i + 1
End If
'
If (i > 2) Then
Exit For
End If
Next
'
rsNames.Edit
rsNames!LastName = strLname
rsNames!FirstName = strFname
rsNames!MiddleInitial = strMI
rsNames.Update
rsNames.MoveNext
Loop
End If
rsNames.Close
Set rsNames = Nothing
We use a loop to find non empty split strings as LastName, FirstName and Middle initial.
This pure VBA code avoids us to use extra VBScript.RegExp replacement.
I would lean towards using RegEx and Split:
Private Sub Test()
Dim strFullName As String
Dim NameParts As Variant
strFullName = "Jones John Q"
With CreateObject("vbscript.regexp")
.Pattern = "\s+"
.Global = True
strFullName = .Replace(strFullName, " ")
End With
NameParts = Split(strFullName, " ")
End Sub
NameParts is an array containing Last, First, and possibly Middle names.
Are First Name and Last Name always in the same position? If so, the use of split can be use to determine the existence of the middle, i may be missing something though, i'd go for
Dim a() As String
a() = Split(s, Chr(32))
strLastName = a(0)
strFirstName = a(1)
If UBound(a) = 2 Then
strMiddle = a(2)
Else
strMiddle = ""
End If
Debug.Print strFirstName, strMiddle, strLastName
or something a bit less elegant
If Len(s) - Len(Replace(s, Chr(32), "")) = 2 Then
strMiddle = Right(s, Len(s) - InStrRev(s, Chr(32)))
End If
Related
I am writing a VBA code to add +2 to any string of numbers that are put in the function.
It works fine, until it reaches 6 and 7, then it breaks. I really have no clue why that is.
If you are wondering why I am doing this, this is part of an encryption algorithm and it is specifically looking to encrypt digits in a string.
My code is:
Sub AddNumbers()
Dim Nos As String
Dim AddNo As String
Dim Found As Boolean
Dim Split()
Nos = "0-1-2-3-4-5-6-7-8-9-10"
Sheets("Sheet1").Range("U2").Value = Nos
Length = Len(Nos)
ReDim Split(Length)
For i = 1 To Length
Found = False
Split(i) = Mid(Nos, i, 1)
For O = 48 To 55
If Split(i) = Chr(O) Then
Split(i) = Chr(O + 2)
Found = True
Exit For
End If
Next O
If Split(i) = Chr(56) Then
Split(i) = Chr(48)
ElseIf Split(i) = Chr(57) Then
Split(i) = Chr(49)
End If
Next i
AddNo = Join(Split, "")
Sheets("Sheet1").Range("U3").Value = AddNo
End Sub
I would really appreciate an insight to why it is breaking at 6 and 7.
Take me a moment, but you are double adding.
Look at your loop. When you encounter 6 (Char(54)) you add 2 and have 8 (Char(56)).
But then, after your loop you are testing again for same Split(i). Char for 6 and 7 are now accordingly 56 and 57 - so you add another 2 to them.
If Split(i) = Chr(56) And Found = False Then
Split(i) = Chr(48)
ElseIf Split(i) = Chr(57) And Found = False Then
Split(i) = Chr(49)
End If
Use the actual function Split:
Sub AddNumbers()
Dim Nos As String
Dim AddNo As String
Dim Found As Boolean
Dim SplitStr() As String
Nos = "0-1-2-3-4-5-6-7-8-9-10"
Sheets("Sheet1").Range("U2").Value = Nos
SplitStr = Split(Nos, "-")
Dim i As Long
For i = LBound(SplitStr) To UBound(SplitStr)
Dim vlue As String
vlue = StrConv(SplitStr(i), vbUnicode)
Dim substr() As String
substr = Split(Left(vlue, Len(vlue) - 1), vbNullChar)
Dim j As Long
For j = LBound(substr) To UBound(substr)
Select Case substr(j)
Case 8
substr(j) = 0
Case 9
substr(j) = 1
Case Else
substr(j) = substr(j) + 2
End Select
Next j
SplitStr(i) = Join(substr, "")
Next i
AddNo = Join(SplitStr, "-")
Sheets("Sheet1").Range("U3").Value = AddNo
End Sub
The overall problem is that you are using the Chr codes for numbers and not actual numbers. This method only returns 1 digit because a Chr() refers to a list of single characters.
You are going to need to use Split (mySplit = Split(Nos,"-")) to return each number and work with those.
The lines
If Split(i) = Chr(56) Then
Split(i) = Chr(48)
ElseIf Split(i) = Chr(57) Then
Split(i) = Chr(49)
End If
has me confused. You are saying if the value is "8" change to "0" and if it is "9" change to "1"
This is another way to do it:
Sub AddNumbers()
Dim Nos As String, Nos2 As String
Dim NumSplit As Variant
Dim Num As Variant
Dim tmp As String
Dim i As Long
Nos = "0-1-2-3-4-5-6-7-8-9-10"
Sheets("Sheet1").Range("U2").Value = Nos
NumSplit = Split(Nos, "-")
For Each Num In NumSplit
For i = 1 To Len(Num)
tmp = tmp & Mid(Num, i, 1) + 2
Next i
Nos2 = Nos2 & tmp & "-"
tmp = ""
Next Num
Nos2 = Left(Nos2, Len(Nos2) - 1)
Sheets("Sheet1").Range("U3").Value = Nos2
End Sub
It's a bit messy, but shows the basic idea of splitting the original array into the separate numbers.
The For....Next loop inside the For...Each loop takes care of any numbers with more than one digit (giving the 32).
I want to sort an array, or the Files from a Filesystemobject Folder, the way we'd expect them to be if sorted by a human. What I ultimately am trying to accomplish is a macro that takes images from a folder and inserts them into the word document with text above each one to identify what it represents, here I use steps for a guide and it's crucial that Step 2 come before step 100;
Setting up my test sub;
Sub RunTheSortMacro()
Dim i As Long
Dim myArray As Variant
'Set the array
myArray = Array("Step-1", "Step-2", "Step-10", "Step-15", "Step-9", "Step-20", "Step-100", "Step-8", "Step-7")
'myArray variable set to the result of SortArray function
myArray = SortArray(myArray)
'Output the Array through a message box
For i = LBound(myArray) To UBound(myArray)
MsgBox myArray(i)
Next i
End Sub
Then the only/best sort function I found is really only good for numbers;
Function SortArray(ArrayIn As Variant)
Dim i As Long
Dim j As Long
Dim Temp
'Sort the Array A-Z
For i = LBound(ArrayIn) To UBound(ArrayIn)
For j = i + 1 To UBound(ArrayIn)
If ArrayIn(i) > ArrayIn(j) Then
SrtTemp = ArrayIn(j)
ArrayIn(j) = ArrayIn(i)
ArrayIn(i) = SrtTemp
End If
Next j
Next i
SortArray = ArrayIn
End Function
That function returns the array as;
Step-1,
Step-10,
Step-100,
Step-15,
Step-2,
Step-20,
Step-7,
Step-8,
Step-9
but I want;
Step-1,
Step-2,
Step-7,
Step-8,
Step-9,
Step-10,
Step-15,
Step-20,
Step-100
I thought using StrComp(ArrayIn(i), ArrayIn(j), vbBinaryCompare/vbTextCompare) would be one way to go but they seem to sort the same way. If it's easier, I am only going the array route because I couldn't find a way to sort the input files from;
Set objFSO = CreateObject("Scripting.Filesystemobject")
Set Folder = objFSO.GetFolder(FolderPath)
For Each image In Folder.Files
ImagePath = image.Path
Selection.TypeText Text:=Left(image.Name, Len(image.Name) - 4)
Selection.TypeText Text:=vbCr
'Insert the images into the word document
Application.Selection.EndKey END_OF_STORY, MOVE_SELECTION
Application.Selection.InlineShapes.AddPicture (ImagePath)
Application.Selection.InsertBreak 'Insert a pagebreak
Next
So I was going to break the file name and path into two arrays that I could sort naturally;
Set objFiles = Folder.Files
FileCount = objFiles.Count
ReDim imageNameArray(FileCount)
ReDim imagePathArray(FileCount)
icounter = 0
For Each image In Folder.Files
imageNameArray(icounter) = (image.Name)
imagePathArray(icounter) = (image.Path)
icounter = icounter + 1
Next
but I can't find any reference to natural sorting in VBA.
Update, Additional Details;
I didn't think about the A and B after numbers and everything I search agrees on what "natural sorting" is; 1,2,3,A,B,C; Apple < 1A < 1C < 2. Regex might be good
This was how I achieved this in a python script;
import os
import re
def tryint(s):
try:
return int(s)
except:
return s
def alphanum_key(s):
""" Turn a string into a list of string and number chunks.
"z23a" -> ["z", 23, "a"]
"""
return [ tryint(c) for c in re.split('([0-9]+)', s) ]
def sort_nicely(l):
""" Sort the given list in the way that humans expect.
"""
l.sort(key=alphanum_key)
files = [file for file in os.listdir(".") if (file.lower().endswith('.png')) or (file.lower().endswith('.jpg'))]
files.sort(key=alphanum_key)
for file in sorted(files,key=alphanum_key):
stepname = file.strip('.jpg')
print(stepname.strip('.png')
For VBA I have found that these;
Function SortArray(ArrayIn As Variant)
Dim i As Long
Dim j As Long
Dim Temp1 As String
Dim Temp2 As String
Dim Temp3 As String
Dim Temp4 As String
'Sort the Array A-Z
For i = LBound(ArrayIn) To UBound(ArrayIn)
For j = i + 1 To UBound(ArrayIn)
Temp1 = ArrayIn(i)
Temp2 = ArrayIn(j)
Temp3 = onlyDigits(Temp1)
Temp4 = onlyDigits(Temp2)
If Val(Temp3) > Val(Temp4) Then
ArrayIn(j) = Temp1
ArrayIn(i) = Temp2
End If
Next j
Next i
SortArray = ArrayIn
End Function
Function onlyDigits(s As String) As String
' Variables needed (remember to use "option explicit"). '
Dim retval As String ' This is the return string. '
Dim i As Integer ' Counter for character position. '
' Initialise return string to empty '
retval = ""
' For every character in input string, copy digits to '
' return string. '
For i = 1 To Len(s)
If Mid(s, i, 1) >= "0" And Mid(s, i, 1) <= "9" Then
retval = retval + Mid(s, i, 1)
End If
Next
' Then return the return string. '
onlyDigits = retval
End Function
Give me the numerical sort but not the alphabetical, so 1B is sorting before 1A.
Here's the solution to sort Naturally in VBA
Setup/Testing
Sub RunTheSortMacro()
Dim i As Long
Dim myArray As Variant
'Set the array
myArray = Array("Step 15B.png", "Cat 3.png", "Step 1.png", "Step 2.png", "Step 15C.png", "Dog 1.png", "Step 10.png", "Step 15A.png", "Step 9.png", "Step 20.png", "Step 100.png", "Step 8.png", "Step 7Beta.png", "Step 7Alpha.png")
'myArray variable set to the result of SortArray function
myArray = SortArray(myArray)
For i = LBound(myArray) To UBound(myArray)
Debug.Print myArray(i)
Next
End Sub
This is the only function needed to be called in the main part;
Function SortArray(ArrayIn As Variant)
Dim i As Long
Dim j As Long
Dim Temp1 As String
Dim Temp2 As String
Dim myRegExp, myRegExp2, Temp3, Temp4, Temp5, Temp6, regExp1_Matches, regExp2_Matches
'Number and what's after the number
Set myRegExp = CreateObject("vbscript.regexp")
myRegExp.IgnoreCase = True
myRegExp.Global = True
myRegExp.pattern = "[0-9][A-Z]"
'Text up to a number or special character
Set myRegExp2 = CreateObject("vbscript.regexp")
myRegExp2.IgnoreCase = True
myRegExp2.Global = True
myRegExp2.pattern = "^[A-Z]+"
'Sort by Fisrt Text and number
For i = LBound(ArrayIn) To UBound(ArrayIn)
For j = i + 1 To UBound(ArrayIn)
Temp1 = ArrayIn(i)
Temp2 = ArrayIn(j)
Temp3 = onlyDigits(Temp1)
Temp4 = onlyDigits(Temp2)
Set regExp1_Matches = myRegExp2.Execute(Temp1)
Set regExp2_Matches = myRegExp2.Execute(Temp2)
If regExp1_Matches.Count = 1 And regExp2_Matches.Count = 1 Then 'eliminates blank/empty strings
If regExp1_Matches(0) > regExp2_Matches(0) Then
ArrayIn(j) = Temp1
ArrayIn(i) = Temp2
ElseIf regExp1_Matches(0) = regExp2_Matches(0) Then
If Val(Temp3) > Val(Temp4) Then
ArrayIn(j) = Temp1
ArrayIn(i) = Temp2
End If
End If
End If
Next j
Next i
'Sort the array again by taking two at a time finds number followed by letters and sorts the two alphabetically, ex 1A, 1B
For i = LBound(ArrayIn) To (UBound(ArrayIn) - 1)
j = i + 1
Temp1 = ArrayIn(i)
Temp2 = ArrayIn(j)
Set regExp1_Matches = myRegExp.Execute(Temp1)
Set regExp2_Matches = myRegExp.Execute(Temp2)
If regExp1_Matches.Count = 1 And regExp2_Matches.Count = 1 Then
If regExp1_Matches(0) > regExp2_Matches(0) Then
ArrayIn(j) = Temp1
ArrayIn(i) = Temp2
End If
End If
Next i
SortArray = ArrayIn
End Function
Found this was useful for the numerical sorting;
Function onlyDigits(s As String) As String
' Variables needed (remember to use "option explicit"). '
Dim retval As String ' This is the return string. '
Dim i As Integer ' Counter for character position. '
' Initialise return string to empty '
retval = ""
' For every character in input string, copy digits to '
' return string. '
For i = 1 To Len(s)
If Mid(s, i, 1) >= "0" And Mid(s, i, 1) <= "9" Then
retval = retval + Mid(s, i, 1)
End If
Next
' Then return the return string. '
onlyDigits = retval
End Function
Results
Input:
Step 15B.png
Cat 3.png
Step 1.png
Step 2.png
Step 15C.png
Dog 1.png
Step 10.png
Step 15A.png
Step 9.png
Step 20.png
Step 100.png
Step 8.png
Step 7Beta.png
Step 7Alpha.png
Output:
Cat 3.png
Dog 1.png
Step 1.png
Step 2.png
Step 7Alpha.png
Step 7Beta.png
Step 8.png
Step 9.png
Step 10.png
Step 15A.png
Step 15B.png
Step 15C.png
Step 20.png
Step 100.png
I would like to have a block of code or a function in vb.net which can write a sentence backwards.
For example : i love visual basic
Result : basic visual love i
This is what I have so far:
Dim name As String
Dim namereversed As String
name = RichTextBox1.Text
namereversed = ""
Dim i As Integer
For i = Len(name) To 1 Step -1
namereversed = namereversed & Replace(name, i, 1)
Next
RichTextBox2.Text = namereversed
The code works but it does not give me the value of what i want. it makes the whole words reversed.
Dim name As String = "i love visual basic"
Dim reversedName As String = ""
Dim tempName As String = ""
For i As Integer = 0 To name.Length - 1
If Not name.Substring(i, 1).Trim.Equals("") Then
tempName += name.Substring(i, 1)
Else
reversedName = tempName + " " + reversedName
tempName = ""
End If
Next
start from index 0 and deduct 1 from length because length count starts with one but index count starts with zero. if you put To name.Length it will return IndexOutOfBounds. Loop it from 0 To Length-1 because you need the word as is and not spelled backwards... what are placed in reverse are the words so add a temporary String variable that stores every word and add it before the saved sentence/words.
or use this
Dim strName As String() = name.Split(" ")
Array.Reverse(strName)
reversedName = String.Join(" ", strName)
This is my contribution, well as you can see its not hard to do, its really simple. There are a lot of other ways which are more short.
Console.Title = "Text Reverser"
Console.ForegroundColor = ConsoleColor.Green
'Text which will be Reversed
Dim Text As String
Console.Write("Write your text: ")
Text = Console.ReadLine
Console.Clear()
Dim RevText As String = "" '← The Text that will be reversed
Dim Index As Int32 = Text.Length '← Index used to write backwards
'Fill RevText with a char
Do Until RevText.Length = Text.Length
RevText = RevText.Insert(0, "§")
Loop
Console.WriteLine(RevText)
'Replace "Spaces" with Character, using 'Index' to know where go the chars
For Each Caracter As Char In Text
Index -= 1 'Rest 1 from the Index
RevText = RevText.Insert(Index, Caracter) '← Put next char in the reversed text
'↓ Finished reversing the text
If Index = 0 Then
RevText = RevText.Replace("§", "") 'Replace char counter to nothing
Console.WriteLine("Your text reversed: " & RevText) '← When Index its 0 then write the RevText
End If
Next
'Pause
Console.ReadKey()
I've done this project in a console, but you know, you can use this code in a normal Windows Form.
This is my first Answer in Stackoverflow :)
Dim keys1() As String = {"corrupt", "selfish", "power", "lying", "lies", "media"}
Dim terms1 As Integer = 0
Dim terms1string As String = ""
terms1string = Console.ReadLine()
For Each st As String In keys1
terms1 = terms1 + 1
Next
If terms1 < 2 Then
Console.WriteLine("yay!")
Else
Console.WriteLine("YouFail")
End If
Theres my code. I'd like it to be that if your string entered has more than two of those terms, then it writes "Yay"-- otherwise it writes "YouFail."
---update 8/29/12---
Function StageTwo(ByVal fname, ByVal lname, ByVal city)
Console.WriteLine("Describe the U.S. Government.")
Dim overall As Integer = 0
Dim keys1() As String = {"corrupt", "selfish", "power", "lying", "lies", "media"}
Dim terms1 As Integer = 0
Dim terms1string As String = ""
terms1string = Console.ReadLine()
For Each st As String In keys1
If InStr(terms1string, st) > 0 Then '<<<this line right here!
terms1 = terms1 + 1
End If
Next
If terms1 < 0 Then
Console.WriteLine("yay!")
overall = overall + 1
End If
Console.WriteLine()
Console.WriteLine("Describe the economic status in the U.S.")
Dim keys2() As String = {"broken", "backed", "failed", "skewed", "tilted", "99%", "rigged", "unfair"}
Dim terms2 As Integer = 0
Dim terms2string As String = ""
terms2string = Console.ReadLine()
For Each st As String In keys2
If InStr(terms2string, st) > 0 Then '<<<this line right here!
terms2 = terms2 + 1
End If
Next
If terms2 < 0 Then
Console.WriteLine("yay!")
overall = overall + 1
End If
If overall = 2 Then
Console.WriteLine()
Console.WriteLine("Enter a username.")
Dim username As String = ""
username = Console.ReadLine()
Console.WriteLine("Please wait.")
IsURLValid(username, overall)
Else
Console.WriteLine("Test Failed.")
End If
System.Threading.Thread.Sleep(2000)
End Function
That's my fresh code. Still not working, it's printing test failed after entering corrupt for the first one and broken for the second one. Help again?
Thanks so much guys.
Why so complicated? Just use Count:
Dim keys1() As String = {"corrupt", "selfish", "power", "lying", "lies", "media"}
Dim terms1string = Console.ReadLine()
Dim terms1 = keys1.Count(function(key) terms1string like "*" & key & "*")
If terms1 < 2 Then
Console.WriteLine("yay!")
Else
Console.WriteLine("YouFail")
End If
If you want to match the single words (foobar power lies are 2 matches, foobarpowerlies are 0 matches), you can use this line instead:
Dim terms1 = keys1.Count(function(key) terms1string.Split().Contains(key))
For completeness, here's a regex version:
' generous match ('foobarpowerlies' => 2 matches)
Dim pattern = String.Join("|", keys1)
Dim terms1 = Regex.Matches(terms1string, pattern).Count
or
' strict match using word boundaries ('foobarpowerlies' => 0 matches, but 'foobar power lies' => 2 matches)
Dim pattern = String.Join("|", keys1.Select(function(key) "\b" & key & "\b"))
Dim terms1 = Regex.Matches(terms1string, pattern).Count
Should "Austin Powers" match "power" and should "uncorrupt" match "corrupt"? Assuming "no"
Should "POWER" match "power"? Assuming "yes"
The safest way to do this is with Regex
Function WordCount(keys() As String, terms As String) As Integer
Dim pattern As String = "\b(" + Regex.Escape(keys(0))
For Each key In keys.Skip(1)
pattern += "|" + Regex.Escape(key)
Next
pattern += ")\b"
Return Regex.Matches("terms", pattern, RegexOptions.IgnoreCase).Count
End Function
Sub Main()
Dim keys1() As String = {"corrupt", "selfish", "power", "lying", "lies", "media"}
Dim count As Integer
count = WordCount(keys1, "lying son of a corrupt . . .") ' returns 2
count = WordCount(keys1, "Never caught lying and uncorrupt . . .") ' returns 1
End Sub
The Regex.Escape function ensures that any Regex specific characters in your keys will be escaped, and will not be treated as Regex commands.
The RegexOptions.IgnoreCase option tells it to do a case insensitive match.
The \b is a word boundry, so there must be a word boundary (space, punctuation, new line, start of string, end of string etc) before and after the match.
Putting the keys in this structure (key1|key2|key3) says it can match on key1 or key2 or key3
Hope this helps
I have something for you.
Your father's INSTR(). This is the weapon of a QuickBasic 4.5 hacker. Not as clumsy or random as a regex; an elegant weapon for a more civilized age.
Module Module1
Sub Main()
Dim keys1() As String = {"corrupt", "selfish", "power", "lying", "lies", "media"}
Dim terms1 As Integer = 0
Dim terms1string As String = ""
terms1string = Console.ReadLine()
For Each st As String In keys1
If InStr(terms1string, st) > 0 Then '<<<this line right here!
terms1 = terms1 + 1
End If
Next st
If terms1 < 2 Then
Console.WriteLine("yay!")
Else
Console.WriteLine("YouFail")
End If
Console.ReadKey()
End Sub
End Module
Perhaps too simplistic, but if you use IndexOf, you can change your For loop to:
If Not String.IsNullOrEmpty(terms1string) Then
For Each st As String In keys1
If terms1string.IndexOf(st) <> -1 Then
terms1 = terms1 + 1
End If
Next
End If
It's simplistic in that it doesn't tokenize the input... so words like "corruption" and "belies" will register a match. If you need exact matches, take a look at String.Split to get the input words, and then there are a number of algorithmic options to compare that list to your list of keys.
How to make a style as a bookmark in word 2010?
You won't be able to use most of the text in the document as the bookmark name. It is just illegal to use certain characters in a bookmark name in Word/VBA. It may be possible to add such characters in bookmark names in an XML format of the document, so if it is required, you can ask a separate question.
This feels like way too much code to post on SO. You really need to explain what framework you have in place and tell us where your hurdles are. We can't do this again. "Works for me". If you have any questions though don't hesitate to ask.
Run the "RunMe" macro at the bottom.
Private Function IsParagraphStyledWithHeading(para As Paragraph) As Boolean
Dim flag As Boolean: flag = False
If InStr(1, para.Style, "heading", vbTextCompare) > 0 Then
flag = True
End If
IsParagraphStyledWithHeading = flag
End Function
Private Function GetTextRangeOfStyledParagraph(para As Paragraph) As String
Dim textOfRange As String: textOfRange = para.Range.Text
GetTextRangeOfStyledParagraph = textOfRange
End Function
Private Function BookmarkNameAlreadyExist(bookmarkName As String) As Boolean
Dim bookmark As bookmark
Dim flag As Boolean: flag = False
For Each bookmark In ActiveDocument.Bookmarks
If bookmarkName = bookmark.name Then
flag = True
End If
Next
BookmarkNameAlreadyExist = flag
End Function
Private Function CreateUniqueBookmarkName(bookmarkName As String)
Dim uniqueBookmarkName As String
Dim guid As String: guid = Mid$(CreateObject("Scriptlet.TypeLib").guid, 2, 36)
guid = Replace(guid, "-", "", , , vbTextCompare)
uniqueBookmarkName = bookmarkName & guid
CreateUniqueBookmarkName = uniqueBookmarkName
End Function
Private Function BookmarkIt(rng As Range, bookmarkName As String)
Dim cleanName As String: cleanName = MakeValidBMName(bookmarkName)
If BookmarkNameAlreadyExist(cleanName) Then
cleanName = CreateUniqueBookmarkName(cleanName)
End If
ActiveDocument.Bookmarks.Add name:=cleanName, Range:=rng
End Function
''shamelessly stolen from gmaxey at http://www.vbaexpress.com/forum/showthread.php?t=37674
Private Function MakeValidBMName(strIn As String)
Dim pFirstChr As String
Dim i As Long
Dim tempStr As String
strIn = Trim(strIn)
pFirstChr = Left(strIn, 1)
If Not pFirstChr Like "[A-Za-z]" Then
strIn = "A_" & strIn
End If
For i = 1 To Len(strIn)
Select Case Asc(Mid$(strIn, i, 1))
Case 49 To 58, 65 To 90, 97 To 122
tempStr = tempStr & Mid$(strIn, i, 1)
Case Else
tempStr = tempStr & "_"
End Select
Next i
tempStr = Replace(tempStr, " ", " ")
MakeValidBMName = tempStr
End Function
Sub RunMe()
Dim para As Paragraph
Dim textOfPara As String
For Each para In ActiveDocument.Paragraphs
If IsParagraphStyledWithHeading(para) Then
textOfPara = GetTextRangeOfStyledParagraph(para)
If para.Range.Bookmarks.Count < 1 Then
BookmarkIt para.Range, textOfPara
End If
End If
Next
End Sub