有没有办法使用 ApplescriptObjc 解码 Quoted-Printable 编码?

Is there a way to decode a Quoted-Printable encoding using ApplescriptObjc?

我正在寻找一种方法来解码 HTML 邮件消息中以 Quoted-Printable 编码编码的源代码,想知道这是否可以在 AppleScriptObjC 中完成?

我知道 Python 3 中有一个名为 quopri 的内置模块可以完成这项工作。所以我有一个后备解决方案。只是希望我可以做到这一点而无需在用户机器上安装 Python 3。由于脚本已经内置在 AppleScript 中,我认为 AppleScriptObjC 将是寻找答案的最佳场所。

从网络上搜索,这应该可以解决问题:

use framework "Foundation"

property NSString : class "NSString"

set coded_s to "" -- some quoted-printable string
--convert from applescript text to NSString
set coded_s to NSString's stringWithString:coded_s
-- delete line wraps
set decoded_s to coded_s's stringByReplacingOccurrencesOfString:("=" & return & linefeed) withString:""
-- replace = with percent
set decoded_s to decoded_s's stringByReplacingOccurrencesOfString:"=" withString:"%"
-- decode as percent-escaped string
set decoded_s to decoded_s's stringByReplacingPercentEscapesUsingEncoding:(current application's NSUTF8StringEncoding)
-- convert from NSString to applescript text
decoded_s as text

quoted-printable 编码标准被定义为 RFC 2045 的一部分。

简要概括地说:不可打印的 ASCII 字符被编码为以 "=" 开头的三字符序列,紧接着是两位十六进制数,其计算结果为 ASCII字符代码; "=" 本身以相同的方式编码;行最多但不能超过 76 个字符,如果 "=" 出现在行尾,则它充当连续(或软中断)字符。

我采用了这些规则的超集来确保满足足够的条件,围绕这些条件编写了以下脚本,并“按原样”呈现,没有任何特殊的优化尝试(仍有余地已完成):

#!/usr/bin/env osascript
--------------------------------------------------------------------------------
use framework "Foundation"
use scripting additions
--------------------------------------------------------------------------------
prop NSString: ref current application's NSString

prop rgex: 1024
prop utf8: 4
--------------------------------------------------------------------------------
on qp_decode(input)
        local input

        try # test for existence as a file path
                input as «class fsrf»
                NSString's stringWithContentsOfURL:result ¬
                        encoding:utf8 |error|:(missing value)
        on error
                input
        end try
        
        set qp to __s(result)

        replace(qp, "(?m)\h*$", "")      # Removing trailing whitespace
        replace(qp, "(?sm)=(\R|$)", "")  # Join lines split with a soft-break
        map(qp, "(?i)=[A-F0-9]{2}", char) # Decode escape sequences, i.e. "=HH"
end qp_decode
--------------------------------------------------------------------------------
# "PRIVATE" HANDLERS:
on __s(init)
        ref item 1 of {init}
end __s

to _nsstring_ from _ref given mutability:m as boolean : false
        local _ref, m, t
 
        set t to NSString's stringWithString:_ref
        if m then set t to t's mutableCopy()
        try
                set _ref's contents to t
        end try
        t
end _nsstring_

to replace(_ref, a, b)
        local _ref, a, f, t
 
        set t to _nsstring_ from _ref with mutability

        t's replaceOccurrencesOfString:a withString:b ¬
                options:rgex |range|:{0, t's |length|()}
        try
                set _ref's contents to t as text
        end try
        t as text
end replace

to map(_ref, a, func as handler)
        local _ref, a, t, f

        script
                prop fn: func
                prop t : _nsstring_ from _ref
                prop s : t's mutableCopy()
                prop index: 0

                on next()
                        t's rangeOfString:a options:rgex
                        set [j, n] to the result's [location, |length|]
                        if n = 0 then return the yield()
                        set u to (t's substringWithRange:[j, n]) as text
                        set c to fn(text 2 thru -1 of u)
                        tell s to replaceOccurrencesOfString:u ¬
                                withString:c options:0 |range|:[my index, ¬
                                |length|() - my index]
                        set index to index + j
                        set t to the substringFromIndex_(index) of s
                        
                        next()
                end next

                on yield()
                        try
                                set _ref's contents to s as text
                        end try
                        s as text
                end yield
        end script
        
        result's next()
end map

to base_10 from base_16
        # There's probably a more ASObjC-y way to do this
        local base_16

        set p to 1
        set |ξ| to 0

        script hexadecimal
                prop digits: "123456789ABCDEF"
                prop coefficients: reverse of base_16's characters
        end script

        repeat with x in hexadecimal's coefficients
                set |ξ| to |ξ| + (offset of x in (hexadecimal's digits)) * p
                set p to p * 16
        end repeat

        |ξ|
end denary

to char(k)
        if k's class = text then set k to (base_10 from k)
        character id k
end char
---------------------------------------------------------------------------❮END❯

您将要调用的主要处理程序是 qp_decode(),它采用单个参数,可以是文字字符串或文件路径,该文件被读取并作为 UTF-8 编码字符串处理,然后作为文字字符串参数进行处理。

我从评论线程中注意到您使用 Script Debugger。如果脚本无法编译或执行,我建议 运行 Script Editor 中的脚本(但是,您需要从原始文件再次 copy/paste来源在这里,因为 Script Debugger 在粘贴之前操作剪贴板的内容)。我实际上在编写和调试此脚本时使用了 vimosascript,因此它是原始文本。