使用例外的方式将字符串转换为标题格式

96

Python中是否有一种标准的方法可以将字符串转换为标题格式(即每个单词以大写字母开头,所有其余的大小写字符均为小写),但保留像 "and"、"in" 和 "of" 这样的介词小写不变?


请查看这个答案。它是语言灵活的。 - Waldeyr Mendes da Silva
9个回答

160
这里有几个问题。如果你使用split和join,一些空格字符会被忽略。内置的capitalize和title方法不会忽略空格。
>>> 'There     is a way'.title()
'There     Is A Way'

如果一句话以冠词开头,你不希望标题的第一个单词小写。

记住这些:

import re 
def title_except(s, exceptions):
    word_list = re.split(' ', s)       # re.split behaves as expected
    final = [word_list[0].capitalize()]
    for word in word_list[1:]:
        final.append(word if word in exceptions else word.capitalize())
    return " ".join(final)

articles = ['a', 'an', 'of', 'the', 'is']
print title_except('there is a    way', articles)
# There is a    Way
print title_except('a whim   of an elephant', articles)
# A Whim   of an Elephant

1
@wizzwizz4:str.split 不会考虑连续的空格。re.split 会保留空格。因此,这个函数不会吞掉任何空格。 - dheerosaur
1
你的代码片段对于 title_except('a whim of aN elephant', articles) 这种情况无法正确工作。你可以使用 word.lower() in exceptions 过滤条件来修复它。 - Dariusz Walczak
@程序员,a 不是因为在字母后面而未大写,而是因为它是副标题的第一个单词。要识别副标题,需要看它是否在冒号 : 后面,所以实际上正确的标题应该是 2001: 太空漫游 - Joshua Coady
“is”不是冠词,而是动词,应该大写。 “of”也不是冠词,而是介词,但它不会大写。如果冠词、介词和连词的长度不超过3个字母,并且它们不是标题或副标题的第一个单词,则通常不会将它们大写。有关标题大小写的更多详细信息,请参见:http://blog.apastyle.org/apastyle/2012/03/title-case-and-sentence-case-capitalization-in-apa-style.html - Joshua Coady
@JoshuaCoady,你的意见非常有道理,我之前也考虑过这个问题。然而,问题在于我的主要操作系统 macOS 不支持文件路径中的 : - 我想在一个重命名文件的脚本中使用它。这就是为什么我会将 : 从名称中省略掉(或根据情况用 - 替换)。我认为电影标题通常比较灵活(在标题/命名规范上)。我将查看该文章,并查看我能够将哪些惯例带入代码中。 - ProGrammer
显示剩余4条评论

64

1
titlecase模块在被转换的字符串中包含数字时无法正常工作。 - Troy
1
@Troy,看起来数字问题已经解决了,或者我没有碰到你的边缘情况。例如:titlecase('one 4 two') -> 'One 4 Two'。现在titlecase('1one') -> '1one',但是'1one'.title() -> '1One'。虽然后一种情况是一个边缘情况,但我不确定'1One'是否是正确的标题方式。我也不太担心,不会去查语法书。 - brent.payne
在“321 A BROADWAY STREET”这种情况下不起作用,因为我得到的是“321 a Broadway Street”。使用dheerosaur提出的解决方案会产生“321 A Broadway Street”。 - MoreScratch
同时,它保留了标题中的首字母缩略词不变。'development of innovative TIaSR' 变成了 'Development of Innovative TIaSR'。 - Matthias Arras

24

有以下这些方法:

>>> mytext = u'i am a foobar bazbar'
>>> print mytext.capitalize()
I am a foobar bazbar
>>> print mytext.title()
I Am A Foobar Bazbar

没有小写文章选项。你需要自己编写代码,可能会使用一个你想要转换成小写的文章列表。


Titlecase.py 可以将文章转为小写。 - TRS-80

15

Stuart Colville 制作了一个 Python 版本的 由 John Gruber 编写的 Perl 脚本,用于将字符串转换为标题式大小写,但基于纽约时报风格指南中的规则避免对小词进行大写处理,同时还考虑了几种特殊情况。

这些脚本的一些巧妙之处:

  • 他们将小单词(如if、in、of、on等)大写,但如果输入错误地大写了它们,则会将它们变成小写。

  • 这些脚本假定除了第一个字符以外的大写字母已经正确大写。这意味着他们会保持像“iTunes”这样的单词不变,而不是把它搞成“ITunes”或者更糟糕的是“Itunes”。

  • 他们跳过任何带有行点的单词;“example.com”和“del.icio.us”将保持小写。

  • 他们有硬编码的特殊处理,以应对奇怪的情况,比如“AT&T”和“Q&A”,两者都包含小单词(at和a),这些单词通常应该是小写的。

  • 标题的第一个和最后一个单词始终大写,因此像“Nothing to be afraid of”这样的输入将被转换为“Nothing to Be Afraid Of”。

  • 冒号后面的小单词将被大写。

您可以在这里下载它。


4
capitalize (word)

这应该可以。我以不同的方式理解它。
>>> mytext = u'i am a foobar bazbar'
>>> mytext.capitalize()
u'I am a foobar bazbar'
>>>

好的,如上面的回复所说,您需要制作一个自定义的大写字母:

mytext = u'我是一个foobar bazbar'

def xcaptilize(word):
    skipList = ['a', 'an', 'the', 'am']
    if word not in skipList:
        return word.capitalize()
    return word

k = mytext.split(" ") 
l = map(xcaptilize, k)
print " ".join(l)   

这会输出:
I am a Foobar Bazbar

那不是我想要的。我想要得到“我是Foobar Bazbar”。 - yassin
@Yassin Ezbakhe:我编辑了我的答案,这应该适合你。文章列表可以轻松地从任何字典中获取。 - pyfunc

2

Python 2.7的title方法存在一个缺陷。

value.title()

当值为 Carpenter's Assistant 时,将返回 Carpenter'S Assistant。

最佳解决方案可能是来自 @BioGeek 使用 Stuart Colville 的 titlecase。这也是 @Etienne 提出的相同解决方案。


2
 not_these = ['a','the', 'of']
thestring = 'the secret of a disappointed programmer'
print ' '.join(word
               if word in not_these
               else word.title()
               for word in thestring.capitalize().split(' '))
"""Output:
The Secret of a Disappointed Programmer
"""

标题以大写字母开头,与文章不匹配。

1
使用列表推导式和三元运算符的一行代码
reslt = " ".join([word.title() if word not in "the a on in of an" else word for word in "Wow, a python one liner for titles".split(" ")])
print(reslt)

说明:

for word in "Wow, a python one liner for titles".split(" ") 将字符串分割为列表,并在列表推导式中初始化一个for循环。

word.title() if word not in "the a on in of an" else word 如果不是冠词,则使用内置方法title()将字符串变成标题格式。

" ".join 使用(空格)作为分隔符来连接列表元素。


0

一个重要的情况没有被考虑到,那就是缩写词(如果你明确地提供它们作为例外,python-titlecase 解决方案可以处理缩写词)。我更喜欢避免使用小写字母。采用这种方法,已经大写的缩写词将保持大写。以下代码是 dheerosaur 最初提供的修改版。

# This is an attempt to provide an alternative to ''.title() that works with 
# acronyms.
# There are several tricky cases to worry about in typical order of importance:
# 0. Upper case first letter of each word that is not an 'minor' word.
# 1. Always upper case first word.
# 2. Do not down case acronyms
# 3. Quotes
# 4. Hyphenated words: drive-in
# 5. Titles within titles: 2001 A Space Odyssey
# 6. Maintain leading spacing
# 7. Maintain given spacing: This is a test.  This is only a test.

# The following code addresses 0-3 & 7.  It was felt that addressing the others 
# would add considerable complexity.


def titlecase(
    s,
    exceptions = (
        'and', 'or', 'nor', 'but', 'a', 'an', 'and', 'the', 'as', 'at', 'by',
        'for', 'in', 'of', 'on', 'per', 'to'
    )
):
    words = s.strip().split(' ')
        # split on single space to maintain word spacing
        # remove leading and trailing spaces -- needed for first word casing

    def upper(s):
        if s:
            if s[0] in '‘“"‛‟' + "'":
                return s[0] + upper(s[1:])
            return s[0].upper() + s[1:]
        return ''

    # always capitalize the first word
    first = upper(words[0])

    return ' '.join([first] + [
        word if word.lower() in exceptions else upper(word)
        for word in words[1:]
    ])


cases = '''
    CDC warns about "aggressive" rats as coronavirus shuts down restaurants
    L.A. County opens churches, stores, pools, drive-in theaters
    UConn senior accused of killing two men was looking for young woman
    Giant asteroid that killed the dinosaurs slammed into Earth at ‘deadliest possible angle,’ study reveals
    Maintain given spacing: This is a test.  This is only a test.
'''.strip().splitlines()

for case in cases:
    print(titlecase(case))

运行时,它会产生以下结果:

CDC Warns About "Aggressive" Rats as Coronavirus Shuts Down Restaurants L.A. County Opens Churches, Stores, Pools, Drive-in Theaters
UConn Senior Accused of Killing Two Men Was Looking for Young Woman
Giant Asteroid That Killed the Dinosaurs Slammed Into Earth at ‘Deadliest Possible Angle,’ Study Reveals
Maintain Given Spacing: This Is a Test.  This Is Only a Test.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接