vbscript multi find/replace string in word document - vbscript

I'm trying to create a VB script that will perform the substitution of certain characters in a word document and I managed in the following way:
objSelection.Find.Text = "["
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "q"
objSelection.Find.Execute ,,,,,,,,,,2
objSelection.Find.Text = "{"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "w"
objSelection.Find.Execute ,,,,,,,,,,2
objSelection.Find.Text = "^"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "y"
objSelection.Find.Execute ,,,,,,,,,,2
objSelection.Find.Text = "~"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "z"
objSelection.Find.Execute ,,,,,,,,,,2
objSelection.Find.Text = "]"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "x"
objSelection.Find.Execute ,,,,,,,,,,2
objSelection.Find.Text = "}"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "ć"
objSelection.Find.Execute ,,,,,,,,,,2
objSelection.Find.Text = "#"
objSelection.Find.Forward = TRUE
objSelection.Find.Replacement.Text = "]"
objSelection.Find.Execute ,,,,,,,,,,2
However, the way in which I managed to do is a very slow, especially when increasing the number of characters that need to be replaced when a text file is several MB... So, the script goes through the entire documet for each replace characters and that resulting is very slow execution of script. Is there a possibility of parallel changes several characters in a word document, the script only one pass through the document and depending on which character is encountered, to perform the replacement?
I also tried loading line by line from word document into a variable and comparison of each character with the given character for a replacement which resulted in much slower execution of scripts...
Sorry for my english, i hope i succeeded to explain my problem.
Please help me. Thank you. :)

Could you do something like this?
objSelection.Find.ClearFormatting
With objSelection.Find
.Text = "[\[\{~\]\}\#^0094]" ' ^0094 is "^"
.Replacement.Text = ""
.Forward = True
.Wrap = 1 'wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Do While objSelection.Find.Execute()
Dim t: t = objSelection.Text
Select Case t
Case "["
objSelection.Text = "q"
Case "{"
objSelection.Text = "w"
Case "~"
objSelection.Text = "z"
'... fill in the rest
End Select
objSelection.Move 1
Loop
I think this may only work if you look for and replace single characters, but what this does is match any of the characters you are looking for, checks which one it's found and replaces it with the appropriate character.
This was taken from a recorded macro in Word 2010 but I don't think any conversion to VBScript should be necessary.

Related

Sorting String with Numbers using VB Script

How to Sort String with Numeric values using VB Script?
Below are my strings from each row from a table:
"Test 1 pass dec 2"
"Test 3 fail"
"Test 2 pass jun 4"
"Verified"
"Test 10 pass"
"User Accepted"
I would to like get in below order after sorting(natural order):
"Test 1 pass dec 2"
"Test 2 pass jun 4"
"Test 3 fail"
"Test 10 pass"
"User Accepted"
"Verified"
Ways i have tried so far,
Set oAlist=CreateObject("System.Collections.ArrayList")
oAlist.sort
The ArrayList was sorted in below order based on ASCII which I do not prefer:
"Test 1 pass dec 2"
"Test 10 pass"
"Test 2 pass jun 4"
"Test 3 fail"
"User Accepted"
"Verified"
I have tried this link Sort
and i have no idea how to use AppendFormat in my case.
Note:My given string either completely string or string with numbers(dynamic) so not sure how to use RecordSet or AppendFormat here as I am new to programming.
You can have another example.
Sub Sort
Set rs = CreateObject("ADODB.Recordset")
If LCase(Arg(1)) = "n" then
With rs
.Fields.Append "SortKey", 4
.Fields.Append "Txt", 201, 5000
.Open
Do Until Inp.AtEndOfStream
Lne = Inp.readline
SortKey = Mid(Lne, LCase(Arg(3)), LCase(Arg(4)) - LCase(Arg(3)))
If IsNumeric(Sortkey) = False then
Set RE = new Regexp
re.Pattern = "[^0-9\.,]"
re.global = true
re.ignorecase = true
Sortkey = re.replace(Sortkey, "")
End If
If IsNumeric(Sortkey) = False then
Sortkey = 0
ElseIf Sortkey = "" then
Sortkey = 0
ElseIf IsNull(Sortkey) = true then
Sortkey = 0
End If
.AddNew
.Fields("SortKey").value = CSng(SortKey)
.Fields("Txt").value = Lne
.UpDate
Loop
If LCase(Arg(2)) = "a" then SortColumn = "SortKey ASC"
If LCase(Arg(2)) = "d" then SortColumn = "SortKey DESC"
.Sort = SortColumn
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
ElseIf LCase(Arg(1)) = "d" then
With rs
.Fields.Append "SortKey", 4
.Fields.Append "Txt", 201, 5000
.Open
Do Until Inp.AtEndOfStream
Lne = Inp.readline
SortKey = Mid(Lne, LCase(Arg(3)), LCase(Arg(4)) - LCase(Arg(3)))
If IsDate(Sortkey) = False then
Set RE = new Regexp
re.Pattern = "[^0-9\\\-:]"
re.global = true
re.ignorecase = true
Sortkey = re.replace(Sortkey, "")
End If
If IsDate(Sortkey) = False then
Sortkey = 0
ElseIf Sortkey = "" then
Sortkey = 0
ElseIf IsNull(Sortkey) = true then
Sortkey = 0
End If
.AddNew
.Fields("SortKey").value = CDate(SortKey)
.Fields("Txt").value = Lne
.UpDate
Loop
If LCase(Arg(2)) = "a" then SortColumn = "SortKey ASC"
If LCase(Arg(2)) = "d" then SortColumn = "SortKey DESC"
.Sort = SortColumn
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
ElseIf LCase(Arg(1)) = "t" then
With rs
.Fields.Append "SortKey", 201, 260
.Fields.Append "Txt", 201, 5000
.Open
Do Until Inp.AtEndOfStream
Lne = Inp.readline
SortKey = Mid(Lne, LCase(Arg(3)), LCase(Arg(4)) - LCase(Arg(3)))
.AddNew
.Fields("SortKey").value = SortKey
.Fields("Txt").value = Lne
.UpDate
Loop
If LCase(Arg(2)) = "a" then SortColumn = "SortKey ASC"
If LCase(Arg(2)) = "d" then SortColumn = "SortKey DESC"
.Sort = SortColumn
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
ElseIf LCase(Arg(1)) = "tt" then
With rs
.Fields.Append "SortKey", 201, 260
.Fields.Append "Txt", 201, 5000
.Open
Do Until Inp.AtEndOfStream
Lne = Inp.readline
SortKey = Trim(Mid(Lne, LCase(Arg(3)), LCase(Arg(4)) - LCase(Arg(3))))
.AddNew
.Fields("SortKey").value = SortKey
.Fields("Txt").value = Lne
.UpDate
Loop
If LCase(Arg(2)) = "a" then SortColumn = "SortKey ASC"
If LCase(Arg(2)) = "d" then SortColumn = "SortKey DESC"
.Sort = SortColumn
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
End If
End Sub
Since you are working with strings, you are going to need to write a custom sort function that can parse the test numbers from the strings.
Alternatively, you could pre-process your list and parse the numbers into a separate field, then sort based on that field.
To apply the techniques from here to the problem (using Split instead of a RegExp):
Option Explicit
Dim aInp : aInp = Array( _
"Test 1 pass dec 2" _
, "Test 3 fail" _
, "Test 2 pass jun 4" _
, "Verified" _
, "Test 10 pass" _
, "User Accepted" _
)
WScript.Echo "----- Input:", vbCrLf & Join(aInp, vbCrLf)
Dim aOtp : aOtp = Array( _
"Test 1 pass dec 2" _
, "Test 2 pass jun 4" _
, "Test 3 fail" _
, "Test 10 pass" _
, "User Accepted" _
, "Verified" _
)
WScript.Echo "----- Expected:", vbCrLf & Join(aOtp, vbCrLf)
Dim oNAL : Set oNAL = CreateObject( "System.Collections.ArrayList" )
Dim oSB : Set oSB = CreateObject( "System.Text.StringBuilder" )
Dim sInp, aParts, aWTF
For Each sInp In aInp
aParts = Split(sInp, " ", 3)
Select Case UBound(aParts)
Case 0 ' add 2 blank elms to "verified"
aWTF = aParts
ReDim Preserve aWTF(2)
Case 1 ' put an empty elm in the middle
' aParts = Array( aParts(0), "", aParts(1))
' ==> VBScript runtime error: This array is fixed or temporarily locked
aWTF = Array( aParts(0), "", aParts(1))
Case 2 ' What the doctor ordered
aWTF = aParts
Case Else
Err.Raise "Shit hits fan"
End Select
oSB.AppendFormat_3 "{0}{1,4}{2}", aWTF(0), aWTF(1), aWTF(2)
sInp = oSB.ToString() & "|" & sInp ' dirty trick: append org data th ease 'reconstruction'
oSB.Length = 0
oNAL.Add sInp
Next
oNAL.Sort
ReDim aOut(oNAL.Count - 1)
Dim i
For i = 0 To UBound(aOut)
aOut(i) = Split(oNAL(i), "|")(1)
Next
WScript.Echo "----- Output:", vbCrLf & Join(aOut, vbCrLf)
output:
cscript 37946075.vbs
----- Input:
Test 1 pass dec 2
Test 3 fail
Test 2 pass jun 4
Verified
Test 10 pass
User Accepted
----- Expected:
Test 1 pass dec 2
Test 2 pass jun 4
Test 3 fail
Test 10 pass
User Accepted
Verified
----- Output:
Test 1 pass dec 2
Test 2 pass jun 4
Test 3 fail
Test 10 pass
User Accepted
Verified
Just for fun: The 'same', but using a RegExp (better scaling technique):
...
Dim r : Set r = New RegExp
r.Pattern = "^(\w+\s*)(\d+\s*)?(.*)$"
Dim sInp, m, aParts(2)
Dim i
For Each sInp In aInp
Set m = r.Execute(sInp)
If 1 = m.Count Then
For i = 0 To 2
aParts(i) = m(0).SubMatches(i)
Next
Else
Err.Raise "Shit hits fan"
End If
oSB.AppendFormat_3 "{0}{1,4}{2}", aParts(0), aParts(1), aParts(2)
sInp = oSB.ToString() & "|" & sInp ' dirty trick: append org data to ease 'reconstruction'
...

Run a word vba macro from vb script

How can I call a word vba macro code from a VB Script:
the word vba macro code is under:
Sub find_replace_vik_42216()
Application.ScreenUpdating = False
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "abc"
.Replacement.Text = "def"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
With Selection
If .Find.Forward = True Then
.Collapse Direction:=wdCollapseStart
Else
.Collapse Direction:=wdCollapseEnd
End If
.Find.Execute Replace:=wdReplaceOne
End With
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "pqr"
.Replacement.Text = "xyz"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
With Selection
If .Find.Forward = True Then
.Collapse Direction:=wdCollapseStart
Else
.Collapse Direction:=wdCollapseEnd
End If
.Find.Execute Replace:=wdReplaceOne
End With
Application.ScreenUpdating = True
End Sub
could dear members create a vb script file which contains the above code, so that I call the vb script , in order to run the code.
I have wrecked my brains and troubled google, to no avail. Please help.
Thank you.
Vik
There is an example of VBScript code which opens the document and make two replacements with given options, more compact form of the .Find.Execute method used:
Const wdFindContinue = 1
Const wdReplaceOne = 1
Dim objWord, objDocument
Set objWord = CreateObject("Word.Application")
objWord.Visible = True
Set objDocument = objWord.Documents.Open("C:\test.docx")
With objWord
.ScreenUpdating = False
With .Selection
.Collapse
With .Find
' .Execute(FindText, MatchCase, MatchWholeWord, MatchWildcards, MatchSoundsLike, MatchAllWordForms, Forward, Wrap, Format, ReplaceWith, Replace, MatchKashida, MatchDiacritics, MatchAlefHamza, MatchControl)
' "abc" -> "def"
.Execute "abc", False, False, False, False, False, True, wdFindContinue, False, "def", wdReplaceOne
' "pqr" -> "xyz"
.Execute "pqr", False, False, False, False, False, True, wdFindContinue, False, "xyz", wdReplaceOne
End With
End With
.ScreenUpdating = True
End With

Better way to write "matching balanced parenthesis" program in Ruby

This method is supposed to take a string and detect if the brackets '(' '{' '[' in the string are closing properly with the corresponding (opposite) brackets.
First, is there a more elegant, compact way to write this bit without using all the "or"s (||):
split_array.each do |i|
if (i == "{" || i == "(" || i == "[")
left.push(i)
else (i == "}" || i == ")" || i == "]")
right.push(i)
end
end
My second question is, is this code terrible (see below)? It seems I should be able to write this in way fewer lines, but logically, I haven't come up with another solution (yet.)
The code works for most tests, but it returns false for this test (see all driver tests at bottom): p valid_string?("[ ( text ) {} ]") == true
Any critique would be greatly appreciated!
(also, if there is a better section to post this, please let me know)
Thanks!
def valid_string?(string)
opposites = { "[" => "]", "{" => "}", "(" => ")", "]" => "[", "}" => "{", ")" => "(" }
left = Array.new
right = Array.new
return_val = true
split_array = string.split(//)
split_array.delete_if { |e| e.match(/\s/) }
split_array.each do |i|
if (i == "{" || i == "(" || i == "[")
left.push(i)
else (i == "}" || i == ")" || i == "]")
right.push(i)
end
end
# p left
# p right
left.each_index do |i|
if left[i] != opposites[right[i]]
return_val = false
end
end
return_val
end
p valid_string?("[ ] } ]") == false
p valid_string?("[ ]") == true
p valid_string?("[ ") == false
p valid_string?("[ ( text ) {} ]") == true
p valid_string?("[ ( text { ) } ]") == false
p valid_string?("[ (] {}") == false
p valid_string?("[ ( ) ") == false
-------Updated: After trying some different approaches, my refactor is this:-----------
def valid_string?(str)
mirrored = { "[" => "]", "{" => "}", "(" => ")" }
open_brackets = Array.new
split_str_array = str.split("")
split_str_array.each do |bracket|
if bracket.match(/[\[|\{|\(]/) then open_brackets.push(bracket)
elsif bracket.match(/[\]|\}|\)]/)
return false if mirrored[open_brackets.pop] != bracket
end
end
open_brackets.empty?
end
My approach is as below :
def valid_string?(string)
open_paren = ['[','{','(']
close_paren = [']','}',')']
open_close_hash = {"]"=>"[", "}"=>"{", ")"=>"("}
stack = []
regex = Regexp.union(close_paren+open_paren)
string.scan(regex).each do |char|
if open_paren.include? char
stack.push(char)
elsif close_paren.include? char
pop_val = stack.pop
return false if pop_val != open_close_hash[char]
end
end
open_paren.none? { |paren| stack.include? paren }
end
valid_string?("[ ] } ]") # => false
valid_string?("[ ]") # => true
valid_string?("[ ") # => false
valid_string?("[ (] {}") # => false
valid_string?("[ ( ) ") # => false
valid_string?("[ ( text { ) } ]") # => false
valid_string?("[ ( text ) {} ]") # => true
Algorithm :
Declare a character stack S.
Now traverse the expression string exp.
If the current character is a starting bracket (‘(‘ or ‘{‘ or ‘[') then push it to stack.
If the current character is a closing bracket (')' or '}' or ']') then pop from stack and if the popped character is the matching starting bracket then fine else parenthesis are not balanced.
After complete traversal, if there is some starting bracket left in
stack then “not balanced”
The shortest regex solution is probably:
def valid_string? orig
str = orig.dup
re = /\([^\[\](){}]*\)|\[[^\[\](){}]*\]|\{[^\[\](){}]*\}/
str[re] = '' while str[re]
!str[/[\[\](){}]/]
end
How about:
class Brackets
def self.paired?(s)
stack = []
brackets = { '{' => '}', '[' => ']', '(' => ')' }
s.each_char do |char|
if brackets.key?(char)
stack.push(char)
elsif brackets.values.include?(char)
return false if brackets.key(char) != stack.pop
end
end
stack.empty?
end
end
Brackets.paired?("[ ] } ]") # => false
Brackets.paired?("[ ]") # => true
Brackets.paired?("[ ") # => false
Brackets.paired?("[ (] {}") # => false
Brackets.paired?("[ ( ) ") # => false
Brackets.paired?("[ ( text { ) } ]") # => false
Brackets.paired?("[ ( text ) {} ]") # => true
You can try this approach:
def balanced_brackets?(string)
# your code here
stack = []
opening_bracket = ['{','[', '(']
closing_bracket = ['}', ']', ')']
string.chars.each do |char|
if opening_bracket.include?(char)
stack << char
elsif closing_bracket.include?(char)
value = stack.pop
return false if opening_bracket.index(value) != closing_bracket.index(char)
end
end
stack.empty?
end
if you want to understand pseudo code try this link from coursera (starts at 0:56).
Another way:
s = str.gsub(/[^\{\}\[\]\(\)]/, '')
while s.gsub!(/\{\}|\[\]|\(\)/, ''); end
s.empty?
Ex 1
str = "(a ()bb [cc{cb (vv) x} c ]ss) "
s = str.gsub(/[^\{\}\[\]\(\)]/, '') #=> "(()[{()}])"
while s.gsub!(/\{\}|\[\]|\(\)/, '') do; end
s => "([{}])" => "([])" => "()" => "" gsub!() => nil
s.empty? #=> true
Ex 2
str = "(a ()bb [cc{cb (vv) x] c }ss) "
s = str.gsub(/[^\{\}\[\]\(\)]/, '') #=> "(()[{()]})"
while s.gsub!(/\{\}|\[\]|\(\)/, '') do; end
s => "([{]})" gsub!() => nil
s.empty? #=> false
This should provide the same functionality
def valid_string?(string)
#assume validity
#valid = true
#empty array will be populated inside the loop
#open_characters = []
#set up a hash to translate the open character to a closing character
translate_open_closed = {"{" => "}","["=>"]","("=>")"}
#create an array from the string loop through each item
string.split('').each do |e|
#adding it to the open_characters array if it is an opening character
#open_characters << e if e=~ /[\[\{\(]/
#if it is a closing character then translate the last open_character to
#a closing character and compare them to make sure characters are closed in order
#the result of this comparison is applied to the valid variable
#valid &= e == translate_open_closed[#open_characters.pop] if e=~ /[\]\}\)]/
end
#return validity and make sure all open characters have been matched
#valid &= #open_characters.empty?
end
You could also do this with inject but it would be a bit less transparent.
I was given this as part of a simulated interview coding challenge. In my case, there was also a parens map passed in { "(" => ")", "[" => "]" }, meaning types of parentheses could vary.
def balanced_parens(string, parens_map)
# where we throw opening parens
opening_parens = []
i = 0
while i < string.length
# if current index is opening paren add to array
if parens_map.keys.include? string[i]
opening_parens << string[i]
# if current index is closing paren, remove last item from opening_array
elsif parens_map.values.include? string[i]
popped_paren = opening_parens.pop
# checking that closing parens at current index is a match for last open parens in opening_array
return false if string[i] != parens_map[popped_paren]
end
i += 1
end
# if opening_parens array is empty, all parens have been matched (&& value = true)
opening_parens.empty?
end
def valid_string?(exp)
return false if exp.size % 2 != 0
curly = "{}"
square = "[]"
parenthesis = "()"
emptystr = ""
loop do
old_exp = exp
exp = exp.sub(curly, emptystr)
break if exp == emptystr
exp = exp.sub(square, emptystr)
break if exp == emptystr
exp = exp.sub(parenthesis, emptystr)
break if exp == emptystr || exp == old_exp
end
exp == emptystr
end

How to capitalize first letter of a string in VBScript

I read a really helpful post here How do I make the first letter of a string uppercase in JavaScript? and I was curious of a good solution using VBScript.
>> s = "first word of string"
>> WScript.Echo UCase(Left(s, 1)) & Mid(s, 2)
>>
First word of string
str = "mY nAME iS sACHIN"
arrStr = split(str," ")
For i=0 to ubound(arrStr)
word = lcase(trim(arrStr(i)))
word = replace(word,mid(word,1,1),chr(asc(mid(word,1,1))-32),1,1)
str1 = str1 & word & " "
next
msgbox trim(str1)
Output: My Name Is Sachin
str = "mY nAME iS sACHIN"
arrStr = split(str," ")
For i=0 to ubound(arrStr)
word = lcase(trim(arrStr(i)))
word = replace(word,mid(word,1,1),ucase(mid(word,1,1)),1,1)
str1 = str1 & word & " "
next
msgbox trim(str1)

String replacing with regexp

I have a regexp that sets $1 : it corresponds to the text between ( and ) in : the_beginning(.*)the_end.
I want to replace the value corresponding to $1 with somethingelse, not all the regexp.
In real context :
my_string contains :
/* MyKey */ = { [code_missing]; MY_VALUE = "123456789"; [code_missing]; }
I want to replace "123456789" ( with "987654321" for example ).
And this is my regexp :
"/\\* MyKey \\*/ = {[^}]*MY_VALUE = \"(.*)\";"
I'm still not sure exactly what you want, but here's some code that should help you:
str = "Hello this is the_beginning that comes before the_end of the string"
p str.sub /the_beginning(.+?)the_end/, 'new_beginning\1new_end'
#=> "Hello this is new_beginning that comes before new_end of the string"
p str.sub /(the_beginning).+?(the_end)/, '\1new middle\2'
#=> "Hello this is the_beginningnew middlethe_end of the string"
Edit:
theDoc = '/* MyKey */ = { [code_missing]; MY_VALUE = "123456789";'
regex = %r{/\* MyKey \*/ = {[^}]*MY_VALUE = "(.*)";}
p theDoc[ regex, 1 ] # extract the captured group
#=> "123456789"
newDoc = theDoc.sub( regex, 'var foo = \1' )
#=> "var foo = 123456789" # replace, saving the captured information
Edit #2: Getting access to information before/after a match
regex = /\d+/
match = regex.match( theDoc )
p match.pre_match, match[0], match.post_match
#=> "/* MyKey */ = { [code_missing]; MY_VALUE = \""
#=> "123456789"
#=> "\";"
newDoc = "#{match.pre_match}HELLO#{match.post_match}"
#=> "/* MyKey */ = { [code_missing]; MY_VALUE = \"HELLO\";"
Note that this requires a regex that does not actually match the pre/post text.
If you need to specify the limits, and not the contents, you can use zero-width lookbehind/lookahead:
regex = /(?<=the_beginning).+?(?=the_end)/
m = regex.match(str)
"#{m.pre_match}--new middle--#{m.post_match}"
#=> "Hello this is the_beginning--new middle--the_end of the string"
…but now this is clearly more work than just capturing and using \1 and \2. I'm not sure I fully understand what you are looking for, why you think it would be easier.

Resources