Yes! It won’t be 5 chars, it will be most likely 50.
It is part of larger project. I am using Ubersicht to display things on my monitor in a window-less and dynamic manner. A kind of personalised “Notification” system.
I have put some part (including my Calendar, To do…) together. See here:
I understand that, but your sample script will put the same five characters for every long paragraph. Is that what you wanted?
I guess that Shane Stanley try to tell you that the correct code would be :
set mytext to "Luciano" & linefeed & "Lucyano_Luciano"
set allParagraphs to paragraphs of mytext
repeat with aParagraph in allParagraphs
if length of aParagraph > 10 then
set contents of aParagraph to text 1 thru 5 of aParagraph -- was wrongly mytext
end if
end repeat
set {TID, text item delimiters} to {text item delimiters, linefeed}
set theResult to allParagraphs as text
set text item delimiters to TID
theResult
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 9 février 2020 10:19:37
Absolutely correct. Thanks Yvan.
I want to replace the first “x” characters of each (long) paragraph.
In that case you could use a regex search/replace:
use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions
set myText to "Luciano" & linefeed & "Lucyano_Luciano"
set myText to current application's NSString's stringWithString:myText
set myText to (myText's stringByReplacingOccurrencesOfString:"(.{5}).{6,}" withString:"$1" options:(current application's NSRegularExpressionSearch) range:{0, myText's |length|()}) as text
Thanks, even tough I do get everything the code does. But I will search for info on stringByReplacingOccurrencesOfString command.
Thanks !
L
I assumed that I understood what it did so I tried a more general syntax:
set beg to 5
set myString to "(.{" & beg & "}).{" & (beg + 1) & ",}"
set myText to (myText's stringByReplacingOccurrencesOfString:(myString) withString:"$1" options:(current application's NSRegularExpressionSearch) range:{0, myText's |length|()}) as text
Alas, it works with beg from 2 thru 7 but doesn’t with beg greater than 7.
What is wrong in my attempt ?
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 9 février 2020 18:51:01
You need to define two variables, something like:
set maxLen to 10 -- alowable length
set trimLen to 5 -- length to trim to
set myString to "(.{" & trimLen & "}).{" & (maxLen - trimLen + 1) & ",}"
set myText to (myText's stringByReplacingOccurrencesOfString:(myString) withString:"$1" options:(current application's NSRegularExpressionSearch) range:{0, myText's |length|()}) as text
Thanks to all. Learning more and more …
L.
Thank you Shane.
Now it’s clear.
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 10 février 2020 08:44:24
I didn’t expect this!!!
1000 repetition of this, takes 5 seconds on my MacBook (2015).
use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions
repeat 1000 times
set myText to "Luciano" & linefeed & "Luciano_Luciano"
set myText to current application's NSString's stringWithString:myText
set myText to (myText's stringByReplacingOccurrencesOfString:"(.{5}).{6,}" withString:"$1" options:(current application's NSRegularExpressionSearch) range:{0, myText's |length|()}) as text
end repeat
While 100000 repetition of this takes 3 seconds. So, 2 order of magnitude faster !!
Is that true or I am doing something wrong …
repeat 100000 times
set mytext to "Luciano" & linefeed & "Luciano_Luciano"
set allParagraphs to paragraphs of mytext
repeat with aParagraph in allParagraphs
if length of aParagraph > 10 then
set contents of aParagraph to text 1 thru 5 of aParagraph -- was wrongly mytext
end if
end repeat
set {TID, text item delimiters} to {text item delimiters, linefeed}
set theResult to allParagraphs as text
set text item delimiters to TID
theResult
end repeat
That sounds reasonable.
You’re only dealing with two paragraphs. I suspect you’ll see different results with 19,000, or even just a couple of thousand.
I ran the two scripts without modification in Script Geek and the timings on my computer were:
ApplescriptObjC - 1.54 seconds
Basic AppleScript - 1.36 seconds
To test something longer, I created a text file with 19000 paragraphs alternating the following:
I then ran the following scripts in Script Geek with only 1 repetition:
use AppleScript version "2.5" -- macOS 10.11 or later
use framework "Foundation"
use scripting additions
tell current application to set myText to read file "Save:Temp:TestFile.txt"
set myText to current application's NSString's stringWithString:myText
set myText to (myText's stringByReplacingOccurrencesOfString:"(.{5}).{6,}" withString:"$1" options:(current application's NSRegularExpressionSearch) range:{0, myText's |length|()}) as text
set myText to read file "Save:Temp:TestFile.txt"
set allParagraphs to paragraphs of myText
repeat with aParagraph in allParagraphs
if length of aParagraph > 10 then
set contents of aParagraph to text 1 thru 5 of aParagraph -- was wrongly mytext
end if
end repeat
set {TID, text item delimiters} to {text item delimiters, linefeed}
set theResult to allParagraphs as text
set text item delimiters to TID
The results reported by Script Geek were:
ApplescriptObjC - 0.27 seconds
Basic AppleScript - 11.84 seconds
This surprised me, so I ran the both scripts in Script Editor. They worked as expected and the times reported by Script Geek seemed ballpark correct.
BTW, the following line only took 4 milliseconds to run:
set myText to read file "Save:Temp:TestFile.txt"
Thanks !
I got similar results with my new iMac. The results I reported earlier were done my my laptop which is older.
L.
ApplescriptObjC - 0.27 seconds
Basic AppleScript - 11.84 seconds
I suspect that latter figure would come down a lot if you used the script object technique, but probably not to near that of the regex approach.
Thanks Shane for the response. I’m not familiar with the script-object technique and google was of little help. I did try utilizing the a-reference-to operator as suggested in the ASLG:
set myText to (paragraphs of (read file "Save:Temp:TestFile.txt"))
set myTextRef to a reference to myText
repeat with aParagraph in myTextRef
if length of aParagraph > 10 then
set contents of aParagraph to text 1 thru 5 of aParagraph -- was wrongly mytext
end if
end repeat
set {TID, text item delimiters} to {text item delimiters, linefeed}
set theResult to myTextRef as text
set text item delimiters to TID
theResult
Utilizing the same text file as before, this script took 0.31 seconds in Script Geek, which is certainly surprising. Just to check, I ran this script in Script Editor, and it worked as expected and took less than a second to complete.
Thanks Shane for the response. I’m not familiar with the script-object technique and google was of little help. I did try utilizing the a-reference-to operator as suggested in the ASLG:
They’re essentially the same technique: the list object being iterated over is passed into the repeat loop by reference, which confers two things: 1. the contents of the list is never evaluated in its entirety as it would be normally. Instead each item is evaluated only as and when its data is retrieved; and 2. an object’s data passed by value has had a copy of its data made, that serves as a snapshot of what that data looked like at that point in time. As it’s a copy, it means that any changes one makes to these data do not affect the original source. However, data passed by reference doesn’t copy the data; it passes the script a pointer to the address at which the data resides. Therefore, when we access these data, they’ll always reflect the data in its most current state; and any changes we make will be to the original source data.
On top of this, access times for list items passed by reference in AppleScript are–as you’ve seen–much, much quicker. Here’s another way to achieve the same thing:
set f to "/tmp/text.poo"
set sentences to the paragraphs of (read f)
repeat with sentence in a reference to my sentences
tell the sentence to if its length > 10 then ¬
set the contents to text 1 thru 5
end repeat
set text item delimiters to linefeed
set textContent to the sentences as text
set eof of f to 0
write the textContent to f
It’s practically identical to your script, but instead of creating a new variable to hold a reference to the list, I just created a reference to it in the declaration of the repeat loop. Here, the reference was created by the word my, which would have been sufficient for speed gains had we not needed to alter the contents of the original list. To make changes, though, a reference to is necessary, or one can use a script object:
script
property path: "/tmp/text.poo"
property list: paragraphs of (read path)
end script
tell the result
repeat with chunk in a reference to its list
tell the chunk to if its length > 10 then set the contents to text 1 thru 5
end repeat
set my text item delimiters to linefeed
set textContent to its list as text
set eof of (its path) to 0
write the textContent to its path
end tell
I got my powers of 2 wrong when creating my text.poo file, which accidentally ended up containing 256,000 lines. It only took about three seconds to read from and write it back to the file.
On a minor note, I’ve observed virtually everyone on this site religiously saving the old tids and then restoring them back afterwards, which I get the sense is something being doing because everyone else is seen to do it. It really isn’t necessary. I think Shane’s gone over this before, but it’s a left-over mantra from back when scripts used to all run within a single AppleScript instance. Now, at least for Script Editor, Script Debugger, and any program calling out to osascript (Alfred, BTT, Keyboard Maestro), this is not the case. The one program I’m aware of (because I use it) that does run successive scripts in a single instance is FastScripts.
Regardless, I would say that a better Good Practice habit to get into would be to forget about storing and resetting them, but always be conscientious to explicitly set the tids immediately before any line in a script that splits text using text items, or joins list items through coercion to text. It uses fewer lines of code; is more readable; it ensures one always stays aware of when these text transformations are taking place; and it alleviates any need to worry about someone else’s script, and whether or not they could have “forgotten” to reset their tids, leaving one free to focus on taking care of their own code in front of them.
Regardless, I would say that a better Good Practice habit to get into would be to forget about storing and resetting them, but always be conscientious to explicitly set the tids immediately before any line in a script that splits text using text items, or joins list items through coercion to text.
We’ve been down this one lots of times before, but let me add my two cents’ worth: I disagree. I think doing both is a safer habit. It’s just too easy, IMO, to use TIDs without realising it.
Thanks CK for the sample scripts and explanations.
I tested your scripts in Script Geek and retested my modification of Yvan’s script, and they all completed in about 0.31 seconds. The second script in your post was fastest but only by a few milliseconds.
When testing your scripts, I did have to preface the variables f and path with file. Otherwise they wouldn’t run. I also removed the two lines that wrote to the source file, just to compare like with like. With these changes, the textContent variable did return the expected results in Script Editor.
The second script in your post is of greatest interest, as it’s new to me and will give me something to study.