I am setting up a modified notification system, which write on my screen the messages I want.
(The differences with respect to “display notification” is that I can decide for how long the messages are visible.
See example using this link: https://imgur.com/dJzokMz
It is based on Übersicht Widgets.
I am relaying in writing/reading stored infos on a text file.
The handler I use is the “classical one”
on WriteTotextFile(fAlias, textToWrite)
try
open for access (fAlias) with write permission
set eof fAlias to 0
write textToWrite to fAlias -- file the_path
close access fAlias -- (file the_path)
on error
close access fAlias -- (file the_path)
end try
end WriteTotextFile
But I find it a bit too slow for my needs.
Which is the fasted way to write/read from a .txt file?
How fast is fast enough? That handler takes about 0.004 seconds here, and if you know the file exists and not already open you can string it down to just the set eof and write lines, which comes in about 0.003 seconds.
Using ASObjC brings it down to under 0.002 seconds.
But if 0.004 is too slow, 0.002 might still not be fast enough.
It might go a smidgen faster if you use the access reference instead of the file or alias specifier:
on WriteTotextFile(fAlias, textToWrite)
try
set fRef to (open for access (fAlias) with write permission)
set eof fRef to 0
write textToWrite to fRef -- file the_path
close access fRef -- (file the_path)
on error
close access fRef -- (file the_path)
end try
end WriteTotextFile
I’ve always saved stuff to a text file pretty much as the OP shows in Post 1. Do I understand correctly that I can safely use the following code if the file in question is not open? The text to be saved is a single line which overwrites existing text in the file. Thanks.
set preferenceFile to "Macintosh HD:Users:peavine:Working:Preference File.txt" as alias
set thePreference to "Preference One"
writePreference(preferenceFile, thePreference)
on writePreference(preferenceFile, thePreference)
set eof preferenceFile to 0
write thePreference to preferenceFile
end writePreference
BTW, I assume that the fastest way to read a text file separate and distinct from writing the text file is:
set preferenceFile to "Macintosh HD:Users:peavine:Working:Preference File.txt" as alias
set preferenceFileText to paragraphs of (read preferenceFile)
That code works if the file both exists and is not already open. It’s the assumption that it’s not already open that’s not particularly safe in a script which cuts corners like that. If the file’s not already open, both commands in the handler will open and close their own access channels, so speedwise, it’s a balancing act between that and opening a single channel once, executing the two commands, and closing the channel once. If the file is already open, the code will only work if the file’s open with write permission and the script happens to get the channel which has the write permission.
Yes. It’s one of those cases where the syntax can fool you. The line isn’t ‘set’-ing the ‘eof’ property of something to 0. It’s a command called ‘set eof’ whose direct parameter is something and labelled parameter ‘to’ is 0.
But putting ‘of’ after the command appears to be harmless, much as it is after ‘count’.
Thanks Nigel. I enjoy learning the behind-the-scenes (balancing-act) stuff that can affect the speed and operation of a script.
I retested the two write alternatives using Script Geek and the cut-the-corners script was about two milliseconds faster on my computer. I suspect that’s not noticeable to the user and, in any event, I try to place text-file writing at the end of the script when all user interaction is done. And, of course, reliability should come first.
on WriteTotextFile(posixPath, textToWrite)
set theText to current application's NSString's stringWithString:textToWrite
return theText's writeToFile:posixPath atomically:true encoding:4 |error|:(missing value) -- NSUTF8StringEncoding
end WriteTotextFile
Here, it’s a shade over twice as fast. It also doesn’t care if you’ve left the file open, although obviously if you do that often enough, you’ll run out of file handles.
Just to report uniform results from one computer, I reran a number of scripts from above in Script Geek and received the results shown below. They are pretty much as expected.
Nigel Full Write Script from Post 3 - 3.3 ms
Peavine Corner-Cutting Write Script from Post 4 - 2.3 ms
As I am curious, I tested my old handler ( minus two parameters) and the ASObjC one.
The test script read a file containing 3 182 570 bytes
then it executed 1000 times the old handler
then it executed 1000 times the ASObjC handler.
I was surprised to see that the old beast was - a very little bit - faster than the new one.
----------------------------------------------------------------
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
----------------------------------------------------------------
on WriteTotextFile(posixPath, textToWrite)
set theText to current application's NSString's stringWithString:textToWrite
return theText's writeToFile:posixPath atomically:true encoding:4 |error|:(missing value) -- NSUTF8StringEncoding
end WriteTotextFile
(*
Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
*)
on writeto(targetFile, theData)
-- targetFile is the file you want to write as «class furl»
-- theData is the data you want in the file.
try
set openFile to open for access targetFile with write permission
set eof openFile to 0
write theData to openFile starting at eof as «class utf8»
close access openFile
return true
on error
try
close access targetFile
end try
return false
end try
end writeto
#=====
set p2d to path to desktop as text
set theSource to (p2d & "Titres m4p 20191215.txt") as alias
set textToWrite to read theSource as «class utf8» -- 3 182 570 bytes
# for old handler
set targetFile to (p2d & "with old handler.txt") as «class furl» # doesn't exist on entry
# for ASObjC handler
set posixPath to POSIX path of (p2d & "with ASObjC.txt") # doesn't exist on entry
set startTime to (time of (current date))
repeat 1000 times
my writeto(targetFile, textToWrite)
end repeat
set endTime1 to (time of (current date))
repeat 1000 times
my WriteTotextFile(posixPath, textToWrite)
end repeat
set endTime2 to (time of (current date))
return {endTime1 - startTime, endTime2 - endTime1} --> {38, 44}
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 16 décembre 2019 17:35:14
I did it twice and get {64, 19} with a “2.534.599 bytes (3,1 MB on disk)” . I might do something wrong …
or maybe the newest handler is optimised for the architecture of new Macs, which in my case is:
iMac (Retina 5K, 27-inch, 2019)
Processo 3,6 GHz 8-Core Intel Core i9
memory 32 GB 2667 MHz DDR4
L.
PS: Just run it again with: 63/19
PS2: I only included a “beep” between the two 1000x repeat loops to receive a feedback when one loop was finished, and the other was starting. Which was confirmed by the appearance of the text file(s) on my desktop.
My machine is :
iMac (21.5 pouces, mi-2011)
Processor 2,8 GHz Intel Core i7
memory 16 Go 1333 MHz DDR3
boot volume: external Thunderbolt 1 SSD with Trim active
running 10.13.6 ( can’t run newer )
I’m surprised by the fact that the old handler is more than two times slower on your machine than on mine.
I guess that your internal SSD is faster than my internal one.
Here Blackmagic Disk Test report:
write : 340 MB/s
read : 380 MB/s
but I doubt that this explain the difference.
I assume that the ASObjC internal routines are enhanced in modern machines ( or in modern OS ).
After all, it’s the engineers’s duty, isn’t it ?
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 16 décembre 2019 19:04:49
The quickest way to read/write data to/from a file is creating RAM disk. (That is, when you want read/write to a file multiple times.)
Create RAM disk on your Mac programatically:
-- Created 2017-01-24 by Takaaki Naganoya
-- 2017 Piyomaru Software
--https://www.tekrevue.com/tip/how-to-create-a-4gbs-ram-disk-in-mac-os-x/
set dName to "RAM Disk"
set dCapacity to 512 * 4 * 1024 --1GB
set aCmd to "diskutil erasevolume HFS+ '" & dName & "' `hdiutil attach -nomount ram://" & (dCapacity as string) & "`"
try
do shell script aCmd
end try
Do write/read operations, as with usual disk, but much faster.
Tell to Finder to eject it, when complete read/write jobs. (Before this, if is need, move the last file from RAM and store on hard disk.)
I apologizes but using a Ram disk isn’t always a valid scheme.
Often, the 16 Gb of my iMac are quite entirely used so Numbers is forced to use SWAP so it works slowly.
If I eat a block of memory with a Ram disk the situation will be worse.
The 16 Gbytes are made of 4 units of 4 Gbytes which were the only size available when I bought the machine.
Of course I would be able to drop that available 16 Gbytes and upgrade to 32 Gbytes but this means buy 32 Gbytes of Ram (180€) for a 8 years old machine. From my point of view, it’s not reasonable.
Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 16 décembre 2019 21:41:28
Hmm…I don’t think the bottleneck is only writing log file.
If you stopped to wrinting log, you can figure out how long does it take to write log to text file.
(1) Frequency of writing to log (to display)
If frequency is high (each file), the total processing time takes long time. You’d better to write log every 10 files, I think.
Or, display “not Normal” status log.
(2)Which is the true bottleneck?
The whole workflow seems to be a ePub checking with ePubchecker (and its alternatives).
Disk performance seems not so bad.
How about CPU performance? CPU thermo?