Есть ли что-то похожее на RegEx в AppleScript, и если нет, то какая альтернатива? - PullRequest
26 голосов
/ 15 июня 2009

Мне нужно проанализировать первые 10 символов имени файла, чтобы увидеть, все ли они цифры. Очевидный способ сделать это - fileName = ~ m / ^ \ d {10} /, но я не вижу ничего regExy в ссылке на appleScript, поэтому мне любопытно, какие еще варианты у меня есть для этой проверки.

Ответы [ 7 ]

24 голосов
/ 17 июля 2009

Не отчаивайтесь, так как с OSX вы также можете получить доступ к sed и grep через "do shell script". Итак:

set thecommandstring to "echo \"" & filename & "\"|sed \"s/[0-9]\\{10\\}/*good*(&)/\"" as string
set sedResult to do shell script thecommandstring
set isgood to sedResult starts with "*good*"

Мои навыки sed не слишком круты, так что может быть более элегантный способ, чем добавить * good * к любому имени, которое соответствует [0-9] {10}, а затем искать * good * в начале результат. Но в основном, если имя файла - «1234567890dfoo.mov», это запустит команду:

echo "1234567890foo.mov"|sed "s/[0-9]\{10\}/*good*(&)/"

Обратите внимание на экранированные кавычки \ "и экранированную обратную косую черту \\ в appleScript. Если вы экранируете вещи в оболочке, вы должны избежать экранирования. Поэтому для запуска сценария оболочки, в котором есть обратная косая черта, вы должны убежать это для оболочки типа \\, а затем экранировать каждый обратный слеш в appleScript вроде \\\\. Это может быть довольно трудно читать.

Так что все, что вы можете сделать в командной строке, вы можете сделать, вызвав ее из applecript (woohoo!). Все результаты на stdout возвращаются в сценарий как результат.

16 голосов
/ 06 сентября 2012

Существует более простой способ использования оболочки (работает на bash 3.2+) для сопоставления с регулярными выражениями:

set isMatch to "0" = (do shell script ¬
  "[[ " & quoted form of fileName & " =~ ^[[:digit:]]{10} ]]; printf $?")

Примечание:

  • Используется современное выражение для тестирования bash [[ ... ]] с оператором сопоставления регулярных выражений, =~; не цитирование правого операнда (или, по крайней мере, специальных символов регулярных выражений) является обязательным в bash 3.2+, если вы не добавите shopt -s compat31;
  • Оператор do shell script выполняет тест и возвращает свою команду выхода с помощью дополнительной команды (спасибо, @LauriRanta); "0" означает успех.
  • Обратите внимание, что оператор =~ не не поддерживает классы символов быстрого доступа, такие как \d, и утверждения, такие как \b (верно для OS X 10.9.4 - это вряд ли изменится в любое время в ближайшее время).
  • Для нечувствительного к регистру соответствия, перед командной строкой введите shopt -s nocasematch;
  • Для locale-осведомленности , перед командной строкой введите export LANG='" & user locale of (system info) & ".UTF-8';.
  • Если регулярное выражение содержит групп захвата , вы можете получить доступ к захваченным строкам через встроенную переменную массива ${BASH_REMATCH[@]}.
  • Как и в принятом ответе, вам придется \ -создать двойные кавычки и обратную косую черту.

Вот альтернативный вариант использования egrep:

set isMatch to "0" = (do shell script ¬
  "egrep -q '^\\d{10}' <<<" & quoted form of filename & "; printf $?")

Хотя это, по-видимому, работает хуже, у него есть два преимущества:

  • Вы можете использовать классы быстрых символов, такие как \d, и утверждения, такие как \b
  • Вы можете легко сделать сопоставление без учета регистра, позвонив egrep с -i:
  • Вы не можете, однако, получить доступ к подматчам через группы захвата; используйте подход [[ ... =~ ... ]], если это необходимо.

Наконец, вот служебные функции , которые объединяют оба подхода (подсветка синтаксиса отключена, но они работают):

# SYNOPIS
#   doesMatch(text, regexString) -> Boolean
# DESCRIPTION
#   Matches string s against regular expression (string) regex using bash's extended regular expression language *including* 
#   support for shortcut classes such as `\d`, and assertions such as `\b`, and *returns a Boolean* to indicate if
#   there is a match or not.
#    - AppleScript's case sensitivity setting is respected; i.e., matching is case-INsensitive by default, unless inside
#      a 'considering case' block.
#    - The current user's locale is respected.
# EXAMPLE
#    my doesMatch("127.0.0.1", "^(\\d{1,3}\\.){3}\\d{1,3}$") # -> true
on doesMatch(s, regex)
    local ignoreCase, extraGrepOption
    set ignoreCase to "a" is "A"
    if ignoreCase then
        set extraGrepOption to "i"
    else
        set extraGrepOption to ""
    end if
    # Note: So that classes such as \w work with different locales, we need to set the shell's locale explicitly to the current user's.
    #       Rather than let the shell command fail we return the exit code and test for "0" to avoid having to deal with exception handling in AppleScript.
    tell me to return "0" = (do shell script "export LANG='" & user locale of (system info) & ".UTF-8'; egrep -q" & extraGrepOption & " " & quoted form of regex & " <<< " & quoted form of s & "; printf $?")
end doesMatch

# SYNOPSIS
#   getMatch(text, regexString) -> { overallMatch[, captureGroup1Match ...] } or {}
# DESCRIPTION
#   Matches string s against regular expression (string) regex using bash's extended regular expression language and
#   *returns the matching string and substrings matching capture groups, if any.*
#   
#   - AppleScript's case sensitivity setting is respected; i.e., matching is case-INsensitive by default, unless this subroutine is called inside
#     a 'considering case' block.
#   - The current user's locale is respected.
#   
#   IMPORTANT: 
#   
#   Unlike doesMatch(), this subroutine does NOT support shortcut character classes such as \d.
#   Instead, use one of the following POSIX classes (see `man re_format`):
#       [[:alpha:]] [[:word:]] [[:lower:]] [[:upper:]] [[:ascii:]]
#       [[:alnum:]] [[:digit:]] [[:xdigit:]]
#       [[:blank:]] [[:space:]] [[:punct:]] [[:cntrl:]] 
#       [[:graph:]]  [[:print:]] 
#   
#   Also, `\b`, '\B', '\<', and '\>' are not supported; you can use `[[:<:]]` for '\<' and `[[:>:]]` for `\>`
#   
#   Always returns a *list*:
#    - an empty list, if no match is found
#    - otherwise, the first list element contains the matching string
#       - if regex contains capture groups, additional elements return the strings captured by the capture groups; note that *named* capture groups are NOT supported.
#  EXAMPLE
#       my getMatch("127.0.0.1", "^([[:digit:]]{1,3})\\.([[:digit:]]{1,3})\\.([[:digit:]]{1,3})\\.([[:digit:]]{1,3})$") # -> { "127.0.0.1", "127", "0", "0", "1" }
on getMatch(s, regex)
    local ignoreCase, extraCommand
    set ignoreCase to "a" is "A"
    if ignoreCase then
        set extraCommand to "shopt -s nocasematch; "
    else
        set extraCommand to ""
    end if
    # Note: 
    #  So that classes such as [[:alpha:]] work with different locales, we need to set the shell's locale explicitly to the current user's.
    #  Since `quoted form of` encloses its argument in single quotes, we must set compatibility option `shopt -s compat31` for the =~ operator to work.
    #  Rather than let the shell command fail we return '' in case of non-match to avoid having to deal with exception handling in AppleScript.
    tell me to do shell script "export LANG='" & user locale of (system info) & ".UTF-8'; shopt -s compat31; " & extraCommand & "[[ " & quoted form of s & " =~ " & quoted form of regex & " ]] && printf '%s\\n' \"${BASH_REMATCH[@]}\" || printf ''"
    return paragraphs of result
end getMatch
11 голосов
/ 08 ноября 2010

Недавно я нуждался в регулярных выражениях в сценарии и хотел найти дополнение к сценариям, чтобы справиться с ним, чтобы было легче читать, что происходит. Я нашел Satimage.osax , который позволяет использовать синтаксис, как показано ниже:

find text "n(.*)" in "to be or not to be" with regexp

Единственным недостатком является то, что (по состоянию на 08.11.2010) это 32-разрядное дополнение, поэтому оно выдает ошибки при вызове из 64-разрядного процесса. Это включило меня в правило Mail для Snow Leopard , так как мне приходилось запускать Mail в 32-битном режиме. Вызывается из автономного скрипта, однако, у меня нет никаких оговорок - это действительно здорово, и позволяет вам выбрать любой синтаксис регулярного выражения , какой вы пожелаете, и использовать обратные ссылки .

Обновление 5/28/2011

Благодаря комментарию модели Митчелла, приведенному ниже, за указание на то, что они обновили его до 64-битного, поэтому больше никаких резервирований - он делает все, что мне нужно.

3 голосов
/ 01 февраля 2014

Вот еще один способ проверить, являются ли первые десять символов любой строки цифрами.

    on checkFilename(thisName)
        set {n, isOk} to {length of fileName, true}
        try
            repeat with i from 1 to 10
                set isOk to (isOk and ((character i of thisName) is in "0123456789"))
            end repeat
            return isOk
        on error
            return false
        end try
    end checkFilename
3 голосов
/ 16 июня 2009

Я уверен, что есть дополнение Applescript или сценарий оболочки, который может быть вызван для добавления регулярных выражений, но я избегаю зависимостей для простых вещей. Я все время использую этот стиль ...

set filename to "1234567890abcdefghijkl"

return isPrefixGood(filename)

on isPrefixGood(filename) --returns boolean
    set legalCharacters to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}

    set thePrefix to (characters 1 thru 10) of filename as text

    set badPrefix to false

    repeat with thisChr from 1 to (get count of characters in thePrefix)
        set theChr to character thisChr of thePrefix
        if theChr is not in legalCharacters then
            set badPrefix to true
        end if
    end repeat

    if badPrefix is true then
        return "bad prefix"
    end if

    return "good prefix"
end isPrefixGood
1 голос
/ 07 июля 2013

У меня есть альтернатива, пока я не реализовал класс символов для алгоритма NFA Томпсона. Я сделал голые работы в AppleScript. Если кто-то заинтересован в поиске синтаксического анализа очень простых регулярных выражений с помощью Applescript, тогда код размещен в CodeExchange на MacScripters, пожалуйста, посмотрите!

Вот решение для определения, если десять первых символов текста / строки:

 set mstr to "1234567889Abcdefg"
set isnum to prefixIsOnlyDigits for mstr
to prefixIsOnlyDigits for aText
    set aProbe to text 1 thru 10 of aText
    set isnum to false
    if not ((offset of "," in aProbe) > 0 or (offset of "." in aProbe) > 0 or (offset of "-" in aProbe) > 0) then
        try
            set aNumber to aProbe as number
            set isnum to true
        end try
    end if
    return isnum
end prefixIsOnlyDigits
0 голосов
/ 02 марта 2019

Мне удалось вызвать JavaScript напрямую из AppleScript (в High Sierra) со следующим:

# Returns a list of strings from _subject that match _regex
# _regex in the format of /<value>/<flags>
on match(_subject, _regex)
    set _js to "(new String(`" & _subject & "`)).match(" & _regex & ")"
    set _result to run script _js in "JavaScript"
    if _result is null or _result is missing value then
        return {}
    end if
    return _result
end match

match("file-name.applescript", "/^\\d+/g") #=> {}
match("1234_file.js", "/^\\d+/g") #=> {"1234"}
match("5-for-fighting.mp4", "/^\\d+/g") #=> {"5"}

Кажется, что большинство JavaScript String методов работают должным образом. Я не нашел справки, для какой версии ECMAScript совместим JavaScript для macOS Automation, поэтому проведите тестирование перед использованием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...