Jak ustalić, czy bieżącym znakiem jest litera

9

Jak mogę ustalić, czy bieżącym znakiem jest litera (znak alfabetyczny) (tzn. Należy do klasy składni [:alpha:]w wyrażeniach regularnych). Chciałbym napisać prostą funkcję jak poniżej:

(defun test-letter () (interactive)
(if char-after-is-a-letter
    (message "This is a letter")
    (message "This is not a letter")
    )
)

Aktualizacja Niestety moje założenie dotyczące równoważności klasy liter i klasy składni [:alpha:]wydaje się fałszywe.

Imię
źródło

Odpowiedzi:

9

Użyj właściwości znaków Unicode

To zdecydowanie powinno działać:

(memq (get-char-code-property (char-after) 'general-category)
      '(Ll Lu Lo Lt Lm Mn Mc Me Nl))

Jako bonus powinien być również szybszy niż looking-at.


Emacs przechowuje wszystkie właściwości znaków określone przez standard Unicode. Są dostępne za pomocą get-char-code-property. W szczególności general-categorywłaściwość określa, które znaki są literami ( Llmałymi, Luwielkimi i nie pytaj mnie, jakie są inne).

Malabarba
źródło
Wielkie dzięki, to rozwiązuje problem, ۱۲۳۴۵۶۷۸۹۰ale istnieją pewne prawdziwe negatywy, np. Arabski lub hebrajski Alef: א, ا.
Imię
@Name Naprawiono. Spróbuj ponownie.
Malabarba
2
Jeszcze raz dziękuję. Sprawdziłem to z różnymi alfabetami i działa. Jedyny wyjątek, jaki znalazłem, dotyczy niektórych alfabetów azjatyckich, takich jak chiński en.wikipedia.org/wiki/Chinese_numerals lub japoński en.wikipedia.org/wiki/Japanese_numerals . Na przykład jest uważany za liczbę 5w języku japońskim. Twój kod uważa to za list. Może to jest litera (jak cyfra rzymska v). Może ktoś, kto zna japoński, może to zweryfikować.
Imię
1
jest jak angielskie słowo five, więc jest literą. Pisząc cyfrę 5 zamiast słowa pięć, używają 5tak jak angielski.
Muir
8

EDYCJA: Ta odpowiedź powinna być całkowicie poprawna w 25.5 (tam, gdzie błąd został naprawiony). W przypadku starszych wersji użyj innej opcji .


To powinno ci powiedzieć, czy obecny znak jest literą i powinien działać w dowolnym języku.

 (looking-at-p "[[:alpha:]]")
Malabarba
źródło
Bardzo dziękuję, jestem ciekawy różnicy między looking-at-pzastosowanym w twoim rozwiązaniu a looking-atdrugą odpowiedzią.
Imię i nazwisko
1
Dwie funkcje są równoważne, z wyjątkiem tego, że looking-at-pnie ustawiają danych dopasowania.
jch
1
@Name looking-at-p jest bliżej czystego predykatu, ponieważ nie ustawia danych dopasowania. Jeśli wcześniej wykonałeś coś w rodzaju wyszukiwania do przodu, match-string(i jego wiele rodzeństwa) zwróci wynik wyszukiwania. Tymczasem w przypadku wersji niep predykatowej łańcuch dopasowania zwróci wynik szukanego dopasowania.
Malabarba
5

Myślę, że możesz uciec od tego:

(defun test-letter ()
  (interactive)
  (let ((char (char-after)))
    (if (and (eq (char-syntax char) ?w)
             (or (> char ?9)
                 (< char ?1)))
        (message "This is a letter")
      (message "This is not a letter"))))

Aktualizacja

Jest to mniej wydajne, ale bliższe temu, czego chcesz:

(defun test-letter ()
  (interactive)
  (if (looking-at "[a-z-A-Z]")
      (message "This is a letter")
    (message "This is not a letter")))
abo-abo
źródło
Dzięki, możliwy problem: Ta funkcja traktuje cyfry (123 ...) jako literę.
Imię i nazwisko
Łatwo naprawić.
abo-abo
Jeszcze raz wielkie dzięki. Kolejny fałszywy wynik pozytywny: uznaje to ۹(tj. Cyfrę indyjską 9) lub ٪za literę.
Imię i nazwisko
1
Twoje pierwsze rozwiązanie było w porządku z greckimi literami (takimi jak ζlub α), ale aktualizacja nie jest.
Imię i nazwisko
Ale połączenie obu jest bliższym rozwiązaniem.
Imię i nazwisko
2

Jeśli bardzo martwiłeś się znakami narodowymi i precyzyjnym traktowaniem klas znaków Unicode, to jedynym rozwiązaniem, jakie udało mi się do tej pory znaleźć, jest regexbiblioteka Python . Zarówno grepi Perl(ku mojemu zdziwieniu!) Nie wykonali poprawnie tej pracy.

Tak więc wyrażenie regularne jesteś po to jest jeden: \p{L}. Jest to znane jako wersja skrócona właściwości Unicode, pełna wersja jest \p{Letter}lub nawet p\{General_Category=Letter}. Lettersam w sobie jest klasą złożoną, ale nie będę wchodził w szczegóły, najlepsze referencje, jakie mogłem znaleźć na ten temat, są tutaj .

Biblioteka języka Python nie jest wbudowana w język (jest alternatywą dla rebiblioteki wbudowanej ). Więc musisz go zainstalować, na przykład:

# pip install regex

Następnie możesz użyć go w następujący sposób:

import regex
>>> regex.match(ur'\p{L}+', u'۱۲۳۴۵۶۷۸۹۰')
>>> regex.match(ur'\p{L}+', u'абвгд')
<regex.Match object; span=(0, 5), match=u'\u0430\u0431\u0432\u0433\u0434'>
>>> regex.match(ur'\p{L}+', u'123')
>>> regex.match(ur'\p{L}+', u'abcd')
<regex.Match object; span=(0, 4), match=u'abcd'>
>>> 

Możesz także umieścić ten skrypt gdzieś, gdzie możesz uzyskać do niego dostęp:

#!/usr/bin/env python
import regex
import sys

if __name__ == "__main__":
    for match in regex.finditer(ur'\p{L}+', sys.argv[1].decode('utf-8')):
        print match.string

I tak to nazwij Emacsem (załóżmy, że zapisałeś ten skrypt ~/bin):

(defun unicode-character-p ()
  (interactive)
  (let* ((current (char-after (point)))
         (result (shell-command-to-string
                  (format "~/bin/is-character.py '%c'" current))))
    (message
     (if (string= result "") "Character %c isn't a letter"
        "Character %c is a letter")
     current)))
wvxvw
źródło