I’m trying to find and replace words in .docx files using fsharp, all the code works without errors yet it is not modifying a word document that i’ve put in the same folder as the project.
Here is my code so far:
open System
#light
#I @“C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20”
#r “Microsoft.Office.Interop.Word.dll”
module FTEST1 =
open Microsoft.Office.Interop.Word
open System.IO
let comarg x = ref (box x)
let printDocument (doc : Document) =
printfn "Printing %s..." doc.Name
let findAndReplace (doc : Document, findText : string, replaceWithText : string) =
printfn "finding and replacing %s..." doc.Name
//options
let matchCase = comarg false
let matchWholeWord = comarg true
let matchWildCards = comarg false
let matchSoundsLike = comarg false
let matchAllWordForms = comarg false
let forward = comarg true
let format = comarg false
let matchKashida = comarg false
let matchDiacritics = comarg false
let matchAlefHamza = comarg false
let matchControl = comarg false
let read_only = comarg false
let visible = comarg true
let replace = comarg 2
let wrap = comarg 1
//execute find and replace
doc.Content.Find.Execute(
comarg findText,
matchCase,
matchWholeWord,
matchWildCards,
matchSoundsLike,
matchAllWordForms,
forward,
wrap,
format,
comarg replaceWithText,
replace,
matchKashida,
matchDiacritics,
matchAlefHamza,
matchControl)
let wordApp = new Microsoft.Office.Interop.Word.ApplicationClass(Visible = true)
let openDocument fileName =
wordApp.Documents.Open(comarg fileName)
// example useage
let closeDocument (doc : Document) =
printfn "Closing %s…" doc.Name
// wdDoNotSaveChanges = 0 | wdPromptToSaveChanges = -2 | wdSaveChanges = -1
doc.Close(SaveChanges = comarg -1)
let findText = "test"
let replaceText = "McTesty"
let findandreplaceinfolders folder findText replaceText =
Directory.GetFiles(folder, "*.docx")
|> Array.iter (fun filePath ->
let doc = openDocument filePath
doc.Activate()
// printDocument doc
printfn "I work"
findAndReplace(doc, findText, replaceText)
closeDocument doc)
let currentFolder = __SOURCE_DIRECTORY__
printfn "Printing all files in [%s]..." currentFolder
findandreplaceinfolders currentFolder
wordApp.Quit()
printfn “Press any key…”
Console.ReadKey(true) |> ignore
Any help would be much appreciated as i’m new to f#. Thanks
Let’s start with checking what Execute
method is returns. You have to assign a result to some variable like this:
let res =
doc.Content.Find.Execute(
comarg findText,
matchCase,
matchWholeWord,
matchWildCards,
matchSoundsLike,
matchAllWordForms,
forward,
wrap,
format,
comarg replaceWithText,
replace,
matchKashida,
matchDiacritics,
matchAlefHamza,
matchControl)
printfn "Result of Execute is: %b" res
Be careful with indentation
Let me know what it prints.
I seem to get a lot of errors from this?
I tried to indent everything correctly though it still gave me some errors for that as well.
Wrong indentations.
res
is belongs to findAndReplace
function so it should be inside of it. You could imagine all these indentations in the next way:
ScriptName.fsx:
- (1) FTEST1 ->
- (2) comarg
- (2) printDocument ->
- (3) printfn “Printing %s…” doc.Name
- (2) findAndReplace ->
- (3) matchCase
- (3) matchWholeWord
- …
- …
The number in brackets is a level.
IDE is clever these days, so it gives you a hint: there is a separator line between each level.
Take a look at the screenshot. Currently definition of your res
is placed at the second level. And where should it be?
Counting: FTEST1
-> findAndReplace
-> … we’re at the right place. It is a third level. So move it to the right side until the line will be straight before let wrap = comarg 1
:
let wrap = comarg 1
//execute find and replace
let res =
...
The same is for printfn "Result of Execute is: %b" res
.
I hope it makes a bit more sense to you now.
Thank you this makes a lot more sense now however it still doesn’t seem to like the let statement.
printfn "Result of ...
should be on the same level as let res
:
Thank you the code is now error free, yet it still doesn’t change the word document, any ideas why?
It wasn’t supposed to change anything. The code above was just for debug purpose - we have to understand where the problem is. So I repeat my Q again:
what does it print to console?
Result of Execute is: true
or Result of Execute is: false
?
It doesn’t print anything, neither true or false.
I see now, you aren’t passing findText
& replaceText
to the findandreplaceinfolders
Replace
findandreplaceinfolders currentFolder
with
findandreplaceinfolders currentFolder findText replaceText
If it won’t work: can you copy the whole console output here?
Here is the whole console output:
open System
- #light
- #I @“C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20”
- #r “Microsoft.Office.Interop.Word.dll”
- module FTEST1 =
-
open Microsoft.Office.Interop.Word
-
open System.IO
-
let comarg x = ref (box x)
-
let printDocument (doc : Document) =
-
printfn "Printing %s..." doc.Name
-
let findAndReplace (doc : Document, findText : string, replaceWithText : string) =
-
printfn "finding and replacing %s..." doc.Name
-
//options
-
let matchCase = comarg false
-
let matchWholeWord = comarg true
-
let matchWildCards = comarg false
-
let matchSoundsLike = comarg false
-
let matchAllWordForms = comarg false
-
let forward = comarg true
-
let format = comarg false
-
let matchKashida = comarg false
-
let matchDiacritics = comarg false
-
let matchAlefHamza = comarg false
-
let matchControl = comarg false
-
let read_only = comarg false
-
let visible = comarg true
-
let replace = comarg 2
-
let wrap = comarg 1
-
//execute find and replace
-
let res =
-
doc.Content.Find.Execute(
-
comarg findText,
-
matchCase,
-
matchWholeWord,
-
matchWildCards,
-
matchSoundsLike,
-
matchAllWordForms,
-
forward,
-
wrap,
-
format,
-
comarg replaceWithText,
-
replace,
-
matchKashida,
-
matchDiacritics,
-
matchAlefHamza,
-
matchControl)
-
printfn "Result of Execute is: %b" res
-
let wordApp = new Microsoft.Office.Interop.Word.ApplicationClass(Visible = true)
-
let openDocument fileName =
-
wordApp.Documents.Open(comarg fileName)
-
// example useage
-
let closeDocument (doc : Document) =
-
printfn "Closing %s…" doc.Name
-
// wdDoNotSaveChanges = 0 | wdPromptToSaveChanges = -2 | wdSaveChanges = -1
-
doc.Close(SaveChanges = comarg -1)
-
let findText = "test"
-
let replaceText = "McTesty"
-
let findandreplaceinfolders folder findText replaceText =
-
Directory.GetFiles(folder, "*.docx")
-
|> Array.iter (fun filePath ->
-
let doc = openDocument filePath
-
doc.Activate()
-
// printDocument doc
-
printfn "I work"
-
findAndReplace(doc, findText, replaceText)
-
closeDocument doc)
-
let currentFolder = __SOURCE_DIRECTORY__
-
printfn "Printing all files in [%s]..." currentFolder
-
findandreplaceinfolders currentFolder findText replaceText
-
wordApp.Quit()
- printfn “Press any key…”
- Console.ReadKey(true) |> ignore
- ;;
→ Added ‘C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20’ to library include path
→ Referenced ‘C:\Users\netha\Documents\FSharpTest\packages\Microsoft.Office.Interop.Word\lib\net20\Microsoft.Office.Interop.Word.dll’ (file may be locked by F# Interactive process)
Printing all files in [c:\Users\netha\Documents\FSharpTest\FTEST]…
I work
finding and replacing ftestdoc.docx…
Result of Execute is: true
Closing ftestdoc.docx…
Press any key…
module FTEST1 = begin
val comarg : x:'a → obj ref
val printDocument : doc:Office.Interop.Word.Document → unit
val findAndReplace :
doc:Office.Interop.Word.Document * findText:string *
replaceWithText:string → unit
val wordApp : Office.Interop.Word.ApplicationClass =
Microsoft.Office.Interop.Word.ApplicationClass
val openDocument : fileName:'a → Office.Interop.Word.Document
val closeDocument : doc:Office.Interop.Word.Document → unit
val findText : string = “test”
val replaceText : string = “McTesty”
val findandreplaceinfolders :
folder:string → findText:string → replaceText:string → unit
val currentFolder : string = “c:\Users\netha\Documents\FSharpTest\FTEST”
end
val it : unit = ()
Thanks for all your help it works now and successfully finds and replaces text. I really appreciate your time