Titrer une chaîne avec des exceptions

87

Est - il un moyen standard en Python pour TitleCase une chaîne ( par exemple les mots commençant par des caractères majuscules, tous les caractères restants ont tubé en minuscules) , mais en laissant des articles tels que and, inet en ofminuscules?

Yassin
la source

Réponses:

151

Il y a quelques problèmes avec cela. Si vous utilisez la division et la jointure, certains caractères d'espacement seront ignorés. Les méthodes intégrées de capitalisation et de titre n'ignorent pas les espaces blancs.

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

Si une phrase commence par un article, vous ne voulez pas que le premier mot d'un titre soit en minuscules.

En gardant cela à l'esprit:

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
dheerosaure
la source
Pourquoi est-ce renécessaire? Il y a une "".splitfonction qui fait la même chose.
wizzwizz4
1
@ wizzwizz4: str.splitne prend pas en compte les espaces contigus. re.splitconserve les espaces. Donc, cette fonction ne mange aucun espace.
dheerosaur
@dheerosaur Je pensais que "".split()cela ne les considérait pas, mais je l'ai "".split(" ")fait.
wizzwizz4
Votre extrait de code ne fonctionnera pas correctement pour la title_except('a whim of aN elephant', articles)casse. Vous pouvez utiliser une word.lower() in exceptionscondition de filtrage pour le corriger.
Dariusz Walczak
@dheerosaur Je cherche un moyen de mettre en majuscule n'importe quel mot qui suit non seulement un article mais aussi un nombre. Pourriez-vous faire un ajout à votre réponse qui le démontre? Par exemple, 2001 a Space Odysseydevrait retourner 2001 A Space Odyssey, où le aest capitalisé comme il suit un nombre. Merci d'avance.
ProGrammer
53

Utilisez le module titlecase.py ! Fonctionne uniquement pour l'anglais.

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

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

Etienne
la source
1
Le module titlecase ne fonctionne pas si la chaîne que vous convertissez contient un nombre n'importe où.
Troy
1
@Troy, il semble que le problème du numéro soit résolu, ou je n'ai pas touché votre cas de pointe. Ex: titlecase ('one 4 two') -> 'One 4 Two'. Maintenant titlecase ('1one') -> '1one', mais '1one'.title () ->' 1One '. bien que ce dernier cas soit un cas limite et je ne suis pas sûr que «1One» soit le titre correct. Je ne suis pas non plus assez préoccupé pour saisir mon livre de grammaire.
brent.payne
Ne fonctionnera pas dans le cas de "321 A BROADWAY STREET" où j'obtiens "321 a Broadway Street". En utilisant la solution proposée par dheerosaur ci-dessus, on obtient "321 A Broadway Street".
MoreScratch
Aussi sympa, cela laisse les acronymes dans le titre intacts. «Développement d'un TIaSR innovant» devient «Développement d'un TIaSR innovant».
Matthias Arras le
22

Il existe ces méthodes:

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

Il n'y a pas d'option d'article en minuscules. Vous devrez le coder vous-même, probablement en utilisant une liste d'articles que vous souhaitez réduire.

nosklo
la source
titlecase.py articles en minuscules.
TRS-80
14

Stuart Colville a fait un portage Python d' un script Perl écrit par John Gruber pour convertir les chaînes en casse de titre mais évite de capitaliser les petits mots basés sur les règles du New York Times Manual of style, ainsi que de traiter plusieurs cas particuliers.

Une partie de l'intelligence de ces scripts:

  • ils mettent en majuscule de petits mots comme if, in, of, on , etc., mais ne les mettront pas en majuscules s'ils sont mis en majuscule par erreur dans l'entrée.

  • les scripts supposent que les mots avec des lettres majuscules autres que le premier caractère sont déjà correctement capitalisés. Cela signifie qu'ils laisseront un mot comme «iTunes» seul, plutôt que de le transformer en «ITunes» ou, pire, «Itunes».

  • ils sautent tous les mots avec des points de ligne; «Example.com» et «del.icio.us» resteront en minuscules.

  • ils ont des hacks codés en dur spécifiquement pour traiter des cas étranges, comme «AT&T» et «Q&A», qui contiennent tous deux de petits mots (at et a) qui devraient normalement être en minuscules.

  • Le premier et le dernier mot du titre sont toujours en majuscules, de sorte qu'une entrée telle que «Rien à craindre» sera transformée en «Rien à craindre».

  • Un petit mot après un deux-points sera mis en majuscule.

Vous pouvez le télécharger ici .

BioGeek
la source
4
capitalize (word)

Cela devrait faire l'affaire. Je comprends les choses différemment.

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

Ok comme dit en réponse ci-dessus, vous devez faire une majuscule personnalisée:

mytext = u'Je suis un 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)   

Cette sortie

I am a Foobar Bazbar
pyfunc
la source
Ce n'est pas ce que je veux. Je veux obtenir "Je suis un Foobar Bazbar"
yassin
@Yassin Ezbakhe: J'ai modifié ma réponse, cela devrait fonctionner pour vous. La liste des articles peut être facilement extraite de n'importe quel dictionnaire
pyfunc
2

La méthode title de Python 2.7 a un défaut.

value.title()

sera de retour « Carpenter S adjoint lorsque la valeur est Carpenter » s adjoint

La meilleure solution est probablement celle de @BioGeek utilisant le titlecase de Stuart Colville. C'est la même solution proposée par @Etienne.

bateau
la source
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
"""

Le titre commence par un mot en majuscule et qui ne correspond pas à l'article.

Tony Veijalainen
la source
1

One-liner utilisant la compréhension de liste et l'opérateur ternaire

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)

Panne:

for word in "Wow, a python one liner for titles".split(" ") Divise la chaîne en une liste et lance une boucle for (dans la liste de compréhension)

word.title() if word not in "the a on in of an" else wordutilise une méthode native title()pour titrer la chaîne s'il ne s'agit pas d'un article

" ".join joint les éléments de la liste avec un séparateur de (espace)

user7297223
la source
0

Un cas important qui n'est pas pris en compte est celui des acronymes (la solution python-titlecase peut gérer les acronymes si vous les fournissez explicitement comme exceptions). Je préfère plutôt éviter simplement le down-tubing. Avec cette approche, les acronymes qui sont déjà en majuscules restent en majuscules. Le code suivant est une modification de celui fourni à l'origine par 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))

Lorsqu'il est exécuté, il produit les éléments suivants:

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.
Août Ouest
la source