python 检查 utf-8 字符串是否为大写

7 投票
3 回答
6367 浏览
提问于 2025-04-16 19:48

我在处理一个utf-8编码的字符串时遇到了.isupper()的问题。我有很多文本文件需要转换成xml格式。虽然文本内容变化很大,但格式是固定的:所有大写的单词应该用<title>标签包裹,而其他的则用<p>标签。这比这复杂得多,但这对于我的问题来说已经足够了。

我的问题是,这个文件是utf-8格式的。这是必须的,因为最终输出中会有很多非英语字符。也许我该给个简单的例子:

输入文本文件:inputText.txt

RÉSUMÉ

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.

期望输出: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>

示例代码:Sample Code

    #!/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 个回答

0

这是一个简单的解决方案。我觉得

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

提到的“词”这个概念和在词边界上使用的 re.split 是我这周早些时候在玩 nltk 时留下的。不过最终我处理的是行,而不是词/标记。这可能会改变,但目前看来这样是有效的。我会暂时保持这个问题开放,希望能得到其他解决方案和评论。

2

正如上面的评论所说,并不是每个字符都能简单地用is lower()和is upper()来区分。有些汉字被认为是“字母”,但它们既不是小写字母,也不是大写字母,更不是标题格式。

所以你提到的要求,即对大写和小写文本进行不同处理,需要进一步说明。我假设你是想区分大写字母和其他所有字符。也许这有点较真,但你确实是在讨论非英语文本。

首先,我建议你在代码中处理字符串时,专门使用Unicode字符串(也就是unicode()这个内置函数)。要养成这样的习惯:把“普通”字符串看作字节字符串,因为它们确实就是字节字符串。所有没有用u"像这样"写的字符串字面量都是字节字符串。

那么这行代码:

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()。根据你发布的内容,似乎你代码的其他部分也需要修改,以便使用字符字符串而不是字节字符串。

9

你发布的代码出错的主要原因是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 问题的看法:

解决这类问题的一般方法是遵循一个简单的标准建议,适用于所有文本问题:尽早将输入从字节串解码为 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 只有语法上的差异——原则(所有处理都在 Unicode 中进行)是一样的。

撰写回答