Classificando uma string com exceções

86

Existe uma maneira padrão em Python para titlecase uma string (ou seja, palavras começar com caracteres maiúsculos, todos os caracteres restantes encaixotado têm minúsculas) mas os artigos que deixam como and, ineof em minúsculas?

Yassin
fonte

Respostas:

149

Existem alguns problemas com isso. Se você usar dividir e juntar, alguns caracteres de espaço em branco serão ignorados. Os métodos incorporados de capitalização e título não ignoram os espaços em branco.

>>> 'There     is a way'.title()
'There     Is A Way'

Se uma frase começa com um artigo, você não quer a primeira palavra de um título em minúsculas.

Tendo isto em mente:

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
dheerossauro
fonte
Por que é renecessário? Existe uma "".splitfunção que faz o mesmo.
wizzwizz4
1
@ wizzwizz4: str.splitnão considera espaços contíguos. re.splitretém espaços. Portanto, esta função não ocupa nenhum espaço.
dheerosaur
@dheerosaur Achei que "".split()não os considerasse, mas "".split(" ")sim.
wizzwizz4
Seu snippet não funcionará corretamente para o title_except('a whim of aN elephant', articles)caso. Você pode usar a word.lower() in exceptionscondição de filtragem para corrigi-lo.
Dariusz Walczak,
@dheerosaur Estou procurando uma maneira de colocar em maiúscula qualquer palavra que segue não apenas um artigo, mas também um número. Você poderia fazer um acréscimo à sua resposta que demonstre isso? Por exemplo, 2001 a Space Odysseydeve retornar 2001 A Space Odyssey, onde a aé maiúscula conforme segue um número. Desde já, obrigado.
ProGrammer
52

Use o módulo titlecase.py ! Funciona apenas para inglês.

>>> from titlecase import titlecase
>>> titlecase('i am a foobar bazbar')
'I Am a Foobar Bazbar'

GitHub: https://github.com/ppannuto/python-titlecase

Etienne
fonte
1
O módulo titlecase não funciona se a string que você está convertendo contiver um número em qualquer lugar dela.
Troy
1
@Troy, parece que o problema com o número foi corrigido, ou eu não acertei seu caso. Ex: titlecase ('um 4 dois') -> 'Um 4 dois'. Agora titlecase ('1one') -> '1one', mas '1one'.title () ->' 1One '. embora este último caso seja um caso extremo e eu não tenha certeza de que '1One' seja a titulação correta. Também não estou preocupado o suficiente para pegar meu livro de gramática.
brent.payne
Não funcionará no caso de "321 A BROADWAY STREET", onde obtenho "321 a Broadway Street". Usando a solução proposta pelo dheerosaur acima produz "321 A Broadway Street".
MoreScratch
Também é bom, ele deixa as siglas no título intactas. 'desenvolvimento de TIaSR inovador' torna-se 'Desenvolvimento de TIaSR inovador'.
Matthias Arras
21

Existem estes métodos:

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

Não há opção de artigo em minúsculas. Você mesmo teria que codificar isso, provavelmente usando uma lista de artigos que deseja reduzir.

nosklo
fonte
artigos em letras minúsculas titlecase.py.
TRS-80 de
13

Stuart Colville fez um port Python de um script Perl escrito por John Gruber para converter strings em maiúsculas, mas evita capitalizar palavras pequenas com base nas regras do Manual de estilo do New York Times, bem como atender a vários casos especiais.

Algumas das habilidades desses scripts:

  • eles capitalizam palavras pequenas como if, in, of, on , etc., mas irão retirá-las se estiverem erroneamente capitalizados na entrada.

  • os scripts pressupõem que as palavras com letras maiúsculas diferentes do primeiro caractere já estão corretamente capitalizadas. Isso significa que eles deixarão uma palavra como “iTunes” sozinha, em vez de transformá-la em “iTunes” ou, pior, “iTunes”.

  • eles pulam quaisquer palavras com pontos de linha; “Example.com” e “del.icio.us” permanecerão em letras minúsculas.

  • eles têm hacks codificados especificamente para lidar com casos ímpares, como “AT&T” e “Q&A”, ambos contendo palavras pequenas (at e a) que normalmente deveriam ser minúsculas.

  • A primeira e a última palavra do título são sempre maiúsculas, portanto, entradas como “Nada a ter medo” serão transformadas em “Nada a ter medo de”.

  • Uma pequena palavra após os dois pontos será maiúscula.

Você pode baixá-lo aqui .

BioGeek
fonte
4
capitalize (word)

Isso deve servir. Eu entendo de forma diferente.

>>> mytext = u'i am a foobar bazbar'
>>> mytext.capitalize()
u'I am a foobar bazbar'
>>>

Ok, como disse na resposta acima, você deve fazer uma capitalização personalizada:

meutexto = eu sou um 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)   

Isso produz

I am a Foobar Bazbar
pyfunc
fonte
Não é isso que eu quero. Eu quero obter "Eu sou um Foobar Bazbar"
yassin
@Yassin Ezbakhe: Editou minha resposta, deve funcionar para você. A lista de artigos pode ser facilmente
obtida
2

O método title do Python 2.7 tem uma falha.

value.title()

retornará Carpenter ' S Assistant quando o valor for Carpenter' s Assistant

A melhor solução é provavelmente a da @BioGeek usando titlecase de Stuart Colville. Qual é a mesma solução proposta por @Etienne.

codificador de barco
fonte
1
 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
"""

O título começa com palavra maiúscula e que não corresponde ao artigo.

Tony Veijalainen
fonte
1

Uma linha usando compreensão de lista e o operador ternário

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)

Demolir:

for word in "Wow, a python one liner for titles".split(" ") Divide a string em uma lista e inicia um loop for (na compreensão da lista)

word.title() if word not in "the a on in of an" else wordusa o método nativo title()para capitalizar a string se não for um artigo

" ".join junta os elementos da lista com um separador de (espaço)

user7297223
fonte
0

Um caso importante que não está sendo considerado são os acrônimos (a solução python-titlecase pode lidar com acrônimos se você os fornecer explicitamente como exceções). Em vez disso, prefiro simplesmente evitar o rebaixamento. Com essa abordagem, as siglas que já estão em maiúsculas permanecem em maiúsculas. O código a seguir é uma modificação daquele originalmente fornecido pelo 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))

Quando executado, ele produz o seguinte:

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.
Agosto oeste
fonte