проверка Python, если строка utf-8 в верхнем регистре - PullRequest
7 голосов
/ 18 июня 2011

У меня проблемы с .isupper (), когда у меня есть строка в кодировке utf-8.У меня много текстовых файлов, которые я конвертирую в xml.Хотя текст очень изменчив, формат статичен.слова во всех заглавных буквах должны быть заключены в теги <title>, а все остальное <p>.Это гораздо сложнее, чем это, но этого должно быть достаточно для моего вопроса.

Моя проблема в том, что это файл utf-8.Это необходимо, так как в окончательном выводе будет несколько много неанглийских символов.Это может быть время, чтобы привести краткий пример:

inputText.txt

RÉSUMÉ

Бекон ipsum dolor sit amet стейк стейка t-кость курица, ирландская земля круглая нострудная ауте панчетта ветчина скакательный сустав аликуа.Долоре короткая поясница с курицей, барабанная палочка, гамбургер и андуй.В трудяшке eiusmod короткая поясница, запасные ребра и колбаса с шариковой головкой.Тендерлойн ут конквотный фланг.Tempor officia, филе дуйс.В pancetta do, тренировка ut dolore t-bone sint свинина pariatur dolore курица.Nostrud ribeye tail, ut ullamco оленина свиная отбивная молочная свинина proidencectetur fugiat preshenderit officia ut tri-tip.

DesiredOutput

    <title>RÉSUMÉ</title>
    <p>Bacon ipsum dolor sit amet strip steak t-bone chicken, irure ground round nostrud
       aute pancetta ham hock incididunt aliqua. Dolore short loin ex chicken, chuck drumstick
       ut hamburger ut andouille. In laborum eiusmod short loin, spare ribs enim ball tip sausage.
       Tenderloin ut consequat flank. Tempor officia sirloin duis. In pancetta do, ut dolore t-bone
       sint pork pariatur dolore chicken exercitation. Nostrud ribeye tail, ut ullamco venison
       mollit pork chop proident consectetur fugiat reprehenderit officia ut tri-tip.
   </p>

Образец кода

    #!/usr/local/bin/python2.7
    # yes this is an alt-install of python

    import codecs
    import sys
    import re
    from xml.dom.minidom import Document

    def main():
        fn = sys.argv[1]
        input = codecs.open(fn, 'r', 'utf-8')
        output = codecs.open('desiredOut.xml', 'w', 'utf-8')
        doc = Documents()
        doc = parseInput(input,doc)
        print>>output, doc.toprettyxml(indent='  ',encoding='UTF-8')

    def parseInput(input, doc):
        tokens = [re.split(r'\b', line.strip()) for line in input if line != '\n'] #remove blank lines

        for i in range(len(tokens)):
            # THIS IS MY PROBLEM. .isupper() is never true.
            if str(tokens[i]).isupper(): 
                 title = doc.createElement('title')
                 tText = str(tokens[i]).strip('[\']')
                 titleText = doc.createTextNode(tText.title())
                 doc.appendChild(title)
                 title.appendChild(titleText)
            else: 
                p = doc.createElement('p')
                pText = str(tokens[i]).strip('[\']')
                paraText = doc.createTextNode(pText)
                doc.appendChild(p)
                p.appenedChild(paraText)

       return doc

if __name__ == '__main__':
    main()

в конечном итоге это довольно просто, я бы принял критические замечания или предложения по моему коду.Кто бы не стал?В частности, я недоволен str(tokens[i]), возможно, есть лучший способ перебрать список строк?

Но цель этого вопроса - найти наиболее эффективный способ проверить, является ли строка utf-8 заглавной.Возможно, мне стоит заняться созданием регулярного выражения для этого.

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

Ответы [ 3 ]

9 голосов
/ 19 июня 2011

Основная причина, по которой ваш опубликованный код дает сбой (даже с использованием только символов ascii!), Заключается в том, что re.split () не будет разбиваться при совпадении с нулевой шириной . r'\b' соответствует нулевым символам:

>>> re.split(r'\b', 'foo-BAR_baz')
['foo-BAR_baz']
>>> re.split(r'\W+', 'foo-BAR_baz')
['foo', 'BAR_baz']
>>> re.split(r'[\W_]+', 'foo-BAR_baz')
['foo', 'BAR', 'baz']

Кроме того, вам необходимо flags=re.UNICODE, чтобы убедиться, что используются определения Unicode \b и \W и т. Д. И использование str() там, где вы это сделали, в лучшем случае не нужно.

Так что это вообще не было проблемой Unicode. Однако некоторые авторы пытались решить эту проблему как проблему Unicode с разной степенью успеха ... вот мое мнение о проблеме Unicode:

Общее решение проблемы такого рода заключается в следовании стандартному простому совету, который применим ко всем текстовым задачам: Как можно раньше расшифруйте свой ввод из строк байтов в строки юникода. Делать всю обработку в юникоде. Закодируйте выводимый юникод в байтовые строки как можно позже.

Итак: byte_string.decode('utf8').isupper() - это путь. Следует избегать таких хаков, как byte_string.decode('ascii', 'ignore').isupper(); они могут быть всеми (сложными, ненужными, подверженными сбоям) - см. ниже.

Некий код:

# coding: ascii
import unicodedata

tests = (
    (u'\u041c\u041e\u0421\u041a\u0412\u0410', True), # capital of Russia, all uppercase
    (u'R\xc9SUM\xc9', True), # RESUME with accents
    (u'R\xe9sum\xe9', False), # Resume with accents
    (u'R\xe9SUM\xe9', False), # ReSUMe with accents
    )

for ucode, expected in tests:
    print
    print 'unicode', repr(ucode)
    for uc in ucode:
        print 'U+%04X %s' % (ord(uc), unicodedata.name(uc))
    u8 = ucode.encode('utf8')
    print 'utf8', repr(u8)
    actual1 = u8.decode('utf8').isupper() # the natural way of doing it
    actual2 = u8.decode('ascii', 'ignore').isupper() # @jathanism
    print expected, actual1, actual2

Вывод из Python 2.7.1:

unicode u'\u041c\u041e\u0421\u041a\u0412\u0410'
U+041C CYRILLIC CAPITAL LETTER EM
U+041E CYRILLIC CAPITAL LETTER O
U+0421 CYRILLIC CAPITAL LETTER ES
U+041A CYRILLIC CAPITAL LETTER KA
U+0412 CYRILLIC CAPITAL LETTER VE
U+0410 CYRILLIC CAPITAL LETTER A
utf8 '\xd0\x9c\xd0\x9e\xd0\xa1\xd0\x9a\xd0\x92\xd0\x90'
True True False

unicode u'R\xc9SUM\xc9'
U+0052 LATIN CAPITAL LETTER R
U+00C9 LATIN CAPITAL LETTER E WITH ACUTE
U+0053 LATIN CAPITAL LETTER S
U+0055 LATIN CAPITAL LETTER U
U+004D LATIN CAPITAL LETTER M
U+00C9 LATIN CAPITAL LETTER E WITH ACUTE
utf8 'R\xc3\x89SUM\xc3\x89'
True True True

unicode u'R\xe9sum\xe9'
U+0052 LATIN CAPITAL LETTER R
U+00E9 LATIN SMALL LETTER E WITH ACUTE
U+0073 LATIN SMALL LETTER S
U+0075 LATIN SMALL LETTER U
U+006D LATIN SMALL LETTER M
U+00E9 LATIN SMALL LETTER E WITH ACUTE
utf8 'R\xc3\xa9sum\xc3\xa9'
False False False

unicode u'R\xe9SUM\xe9'
U+0052 LATIN CAPITAL LETTER R
U+00E9 LATIN SMALL LETTER E WITH ACUTE
U+0053 LATIN CAPITAL LETTER S
U+0055 LATIN CAPITAL LETTER U
U+004D LATIN CAPITAL LETTER M
U+00E9 LATIN SMALL LETTER E WITH ACUTE
utf8 'R\xc3\xa9SUM\xc3\xa9'
False False True

Единственные отличия от Python 3.x - это синтаксический принцип - принцип (вся обработка выполняется в юникоде) остается тем же.

2 голосов
/ 18 июня 2011

Как показывает один комментарий выше, для каждого символа неверно, что одна из проверок islower () против isupper () всегда будет истинной, а другая ложной. Например, символы объединенного хана считаются «буквами», но не строчными, не прописными и не заглавными.

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

Во-первых, я рекомендую использовать строки Unicode (встроенный unicode ()) исключительно для частей кода, обрабатывающих строки. Дисциплинируйте свой разум, чтобы думать о «обычных» строках как о байтовых строках, потому что это именно то, что они есть. Все строковые литералы, не написанные u"like this", являются байтовыми строками.

Эта строка кода:

tokens = [re.split(r'\b', line.strip()) for line in input if line != '\n']

станет:

tokens = [re.split(u'\\b', unicode(line.strip(), 'UTF-8')) for line in input if line != '\n']

Вы бы также протестировали tokens[i].isupper() вместо str(tokens[i]).isupper(). Исходя из того, что вы опубликовали, представляется вероятным, что другие части вашего кода необходимо будет изменить для работы со строками символов вместо строк байтов.

0 голосов
/ 18 июня 2011

Простое решение.Я думаю, что

tokens = [re.split(r'\b', line.strip()) for line in input if line != '\n'] #remove blank lines

становится

tokens = [line.strip() for line in input if line != '\n']

, тогда я могу идти без необходимости str() или unicode() Насколько я могу судить.

if tokens[i].isupper(): #do stuff

Слово token и переразметка границ слова являются наследием того времени, когда я раньше связывался с nltk в начале этой недели.Но в конечном итоге я обрабатываю строки, а не токены / слова.Это может измениться.но сейчас это похоже на работу.Я пока оставлю этот вопрос открытым в надежде на альтернативные решения и комментарии.

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