【问题标题】:python check if utf-8 string is uppercasepython检查utf-8字符串是否为大写
【发布时间】:2011-06-17 20:29:20
【问题描述】:

当我有一个 utf-8 编码的字符串时,我遇到了 .isupper() 的问题。我有很多要转换为 xml 的文本文件。虽然文本变化很大,但格式是静态的。所有大写的单词都应该用<title> 标签和其他所有东西<p> 包装。它比这要复杂得多,但这应该足以解决我的问题。

我的问题是这是一个 utf-8 文件。这是必须的,因为最终输出中会有 some 很多非英文字符。或许是时候提供一个简短的例子了:

inputText.txt

简历

培根 ipsum dolor sit amet 条牛排 丁骨鸡,磨碎的圆形 nostrud aute pancetta 火腿飞节 事件 aliqua。多洛尔短腰 前鸡肉,查克鼓槌 ut 汉堡和安杜耶。在产房 eiusmod 里脊肉,排骨 enim 球尖香肠。里脊肉 结果侧翼。临时官 沙朗duis。在薄饼做,ut dolore t-bone sint 猪肉 pariatur 多洛尔鸡练习。诺斯特鲁德 肋眼尾,ut ullamco 鹿肉莫利特 猪排proident consectetur fugiat reprehenderit office ut tri-tip.

所需输出

    <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

【问题讨论】:

  • 你使用str()而不是unicode()有什么原因吗?
  • isupper() 依赖于 8 位字符串的区域设置;我认为这可能是问题的一部分
  • @tchrist - 根据这个网站,罗马数字字符既不是大写也不是小写,使得 Python 中的 False 结果对于 isupper() 是正确的:fileformat.info/info/unicode/char/216a/index.htm。我没有验证 istitle()
  • 看到人们提升可证明是错误的 cmets 是非常了不起的。我的陈述是正确的,正如 Unicode 标准™ 明确规定的那样,它是这件事的 权威来源。 Python 只是弄错了。问题是,这个 bug 什么时候修复?
  • @tchrist:请发布您的错误修复请求的 URL。

标签: python unicode utf-8


【解决方案1】:

您发布的代码失败(即使只有 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 以确保使用\b\W 等的Unicode 定义。并且在你所做的地方使用str() 充其量是不必要的。

所以它本身根本不是一个真正的 Unicode 问题。然而,一些回答者试图将其作为一个 Unicode 问题来解决,并取得了不同程度的成功......这是我对 Unicode 问题的看法:

此类问题的一般解决方案是遵循适用于所有文本问题的标准 bog-simple 建议:尽早将您的输入从字节字符串解码为 un​​icode 字符串。使用 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 进行所有处理)保持不变。

【讨论】:

  • 感谢您教育我。哈! :)
  • 到目前为止,这个答案已证明对我的问题最有帮助。谢谢。
【解决方案2】:

正如上面的一条评论所说明的,对于每个字符来说,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()。根据您发布的内容,您的代码的其他部分似乎可能需要更改以使用字符串而不是字节字符串。

【讨论】:

  • 在我回到办公室之前我无法测试这个解决方案,但看起来这也可能是一个可行的解决方案。我发布的解决方案有效。但这可能会更好。谢谢。
  • -1 有两个原因:(1) re.split(r'\b', ...) 不起作用。 (2) unicode(blahblah) 依赖于默认编码为 UTF-8 —— 例如,它是 ascii Windows 机器和任何情况下的系统管理员都可以摆弄 site.py 或任何改变它的东西。
  • (1) 它似乎返回输入字符串不变,所以毫无意义,但我不确定“不工作”是否合理 (2) 将编码参数添加到 unicode() 内置在我的回答中
【解决方案3】:

简单的解决方案。我觉得

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 时留下的遗产。但最终我正在处理线条,而不是标记/单词。这可能会改变。但现在这似乎有效。我将暂时保留这个问题,希望有替代解决方案和 cmets。

【讨论】:

    猜你喜欢
    • 2010-12-01
    • 2014-03-18
    • 2016-07-18
    • 2018-09-07
    • 2017-12-24
    • 2021-10-11
    • 2018-09-03
    • 1970-01-01
    • 2013-08-16
    相关资源
    最近更新 更多