I’ve been trying to find a way to create a simple script that will let me automate some find and replace of a string in text edit. I did look around in the different posts but nothing seemed to have worked for me so far.
What I’m trying to do is to replace this string "\ TC: " by this string "\ TC: " (there is some empty space that is causing me trouble)
thanks
Fred
AppleScript: 2.7
Browser: Safari 601.2.7
Operating System: Mac OS X (10.10)
To find and replace text in AppleScript strings is recommened to use AppleScript’s text item delimiters. The performance is faster than using an 3rd party tool (app or osax).
on stringReplace(haystack, needle, replace)
tell AppleScript
set oldTIDs to text item delimiters
set text item delimiters to needle
set lst to text items of haystack
set text item delimiters to replace
set str to lst as string
set text item delimiters to oldTIDs
end tell
return str
end stringReplace
However the question was how to find and replace in text edit application, which can be done in two ways.
tell application "TextEdit"
tell document 1
set its text to my stringReplace(its text, "findstring", "replacestring")
end tell
end tell
on stringReplace(haystack, needle, replace)
tell AppleScript
set oldTIDs to text item delimiters
set text item delimiters to needle
set lst to text items of haystack
set text item delimiters to replace
set str to lst as string
set text item delimiters to oldTIDs
end tell
return str
end stringReplace
Using the example above, the entire markup of the document is ignored and removed. To keep the markup in the document you can remove and insert new characters in text edit to preserve it’s markup instead of replace the entire document’s text.
tell application "TextEdit"
tell document 1
set searchString to "searchString"
set replaceString to "replaceString"
set searchLen to count of searchString
set matchPositions to reverse of my stringOffsets(its text, searchString)
repeat with matchPosition in matchPositions
if searchLen > 1 then
delete (characters (matchPosition + 1) thru (matchPosition + searchLen - 1))
end if
set character matchPosition to replaceString
end repeat
end tell
end tell
on stringOffsets(haystack, needle)
set offsets to {}
set pos to 0
tell AppleScript
set oldTIDs to text item delimiters
set text item delimiters to needle
set lst to text items of haystack
set text item delimiters to oldTIDs
end tell
repeat with x from 1 to (count lst) - 1
set pos to pos + (length of item x of lst)
set end of offsets to pos + 1
set pos to pos + (length of needle)
end repeat
return offsets
end stringOffsets
The code above will be slower but will preserve the markup of the document. When you have AppleScript Toolbox installed you could also avoid the stringOffsets handler and use the AST find regex command instead:
tell application "TextEdit"
tell document 1
set searchString to "searchString"
set replaceString to "replaceString"
set searchLen to count of searchString
set haystack to it's text
tell current application
set matchPositions to reverse of (AST find regex searchString in string haystack with returning offsets)
end tell
repeat with matchPosition in matchPositions
if (rm_eo of matchPosition) - (rm_so of matchPosition) > 1 then
delete (characters ((rm_so of matchPosition) + 1) thru ((rm_eo of matchPosition) - 1))
end if
set character ((rm_so of matchPosition) + 1) to replaceString
end repeat
end tell
end tell
Alternate version using the same scheme than DJ Bazzie Wazzie one but is case sensitive.
Honestly I don’t know which one is faster.
My proposal uses the library BridgePlus written by Shane STANLEY.
use scripting additions
use framework "Foundation"
use script "BridgePlus"
load framework
set searchString to "searchString"
set replaceString to "replaceString"
set searchLen to count of searchString
tell application "TextEdit"
tell document 1
set matchPositions to reverse of my stringOffsets(its text, searchString)
repeat with matchPosition in matchPositions
if searchLen > 1 then
delete (characters (matchPosition + 1) thru (matchPosition + searchLen - 1))
end if
set character matchPosition to replaceString
end repeat
end tell
end tell
on stringOffsets(haystack, needle)
set theResult to current application's SMSForder's findMatchRecords:needle inString:haystack options:""
set fullList to ASify from theResult
--set fullList to theResult as list -- only in 10.10 and later
set theOffsets to {}
repeat with aRecord in fullList
set end of theOffsets to location of foundRange of aRecord
end repeat
return theOffsets
end stringOffsets
Here it uses ASify. If you are running 10.10 or 10.11, you may disable set fullList to ASify from theResult and enable set fullList to theResult as list.
use AppleScript version "2.3.1"
use scripting additions
use framework "Foundation"
use framework "AppKit"
set posixPath to POSIX path of (choose file of type {"rtf"})
set searchString to "searchString"
set replaceString to "replaceString"
-- read in file as attributed string
set {theStyledString, docAttributes} to current application's NSAttributedString's alloc()'s initWithPath:posixPath documentAttributes:(reference)
-- make copy you can modify
set theStyledString to theStyledString's mutableCopy()
-- get it's text contents and length
set theText to theStyledString's |string|()
set fullLength to theText's |length|()
set theLength to fullLength
repeat
-- search for the string starting at the end
set theRange to theText's rangeOfString:searchString options:(current application's NSBackwardsSearch) range:{0, theLength}
if |length| of theRange = 0 then exit repeat -- none found, so exit
-- use the range to replace the characters
theStyledString's replaceCharactersInRange:theRange withString:replaceString
-- adjust the length you're searching in
set theLength to location of theRange
end repeat
-- turn attributed string into RTF data
set theData to theStyledString's RTFFromRange:{0, theStyledString's |length|()} documentAttributes:docAttributes
-- build path for new file
set posixPath to current application's NSString's stringWithString:posixPath
set newPath to (posixPath's stringByDeletingPathExtension()'s stringByAppendingString:"-copy")'s stringByAppendingPathExtension:(posixPath's pathExtension())
-- write the data to the file
theData's writeToFile:newPath atomically:true
Have you actually tested your pure AppleScript solution against the Satimage.osax solution?
If so, I’d love to see the test and the results.
IAC, ease of use may outweigh performance benefits. Unless you are in a tight loop doing hundred’s, or maybe thousands, of find/replace, I submit the Satimage.osax solution will be just as good, and the difference in performance not noticeable/material.
Every osax command uses an AppleEvent. It has nothing to do with the osax command itself but the overhead of the AppleEvent manager. It became clear how large the overhead was when I started to write osaxen myself because it became also clear how slow getting and creating AppleEvent types was in an programming language that is known for it’s high performance. So it’s not something against Satimage.osax it’s something against all osaxen including my own.
However for very quick simple stand alone tasks such as yours I would use Text Wrangler
do shell script “open -n -a ‘TextWrangler.app’”
tell application “TextWrangler”
open “PathtoFile.txt”
replace "\ TC: " using "\ TC: " searching in text of text document 1 options {starting at top:true}
save documents
close documents
quit
end tell
You are assuming that the document is a text file but most of the time, the default format for TextEdit document is not .txt but .rtfd.
In this late format, it’s fine to keep the strings attributes unchanged.
Shane’s proposal take care of that. If I understand well, yours strip all attributes and return a plain text file.
Of course, only the Original Poster may tell us which kind of file he really use.
Not completely unimportant is that the TS wanted it to work with TextEdit, Shane’s solution bypasses TextEdit completely uncertain if that is a good thing or not.
Honestly, I don’t know what was the real link with TextEdit.
Was it that the asker wanted a piece of code to complete a script working upon a TextEdit file ?
Was it just wanting to make changes in a .txt , a.rtf or a .rtfd file ?
In this late option, we just need to edit one instruction in Shane’s code :
set posixPath to POSIX path of (choose file of type {"rtf", "rtfd", "txt"})
and the script will be able to treat the three formats.
As I wrote in an other thread, I’m not a sooth sayer, so most of the time like other helpers I “try” to answer a question which is far from a precise one opening the door to many schemes.
I guess that I will be buried before you see a majority of questions describing completely and accurately the asker’s problem. It seems that we have to live with that.