Titlecasing ciągu z wyjątkami

87

Czy istnieje standardowy sposób w Pythonie do titlecase ciąg (tj słowa zaczynają się wielkimi literami, wszystkie pozostałe obudowane znaki mają małe litery), ale pozostawiając artykułów takich jak and, ini ofmałe litery?

yassin
źródło

Odpowiedzi:

151

Jest z tym kilka problemów. Jeśli używasz dzielenia i łączenia, niektóre znaki odstępu zostaną zignorowane. Wbudowane metody wielkich liter i tytułu nie ignorują białych znaków.

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

Jeśli zdanie zaczyna się od przedimka, nie chcesz, aby pierwsze słowo tytułu było pisane małymi literami.

Mając to na uwadze:

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
dheerozaur
źródło
Dlaczego jest to rekonieczne? Jest "".splitfunkcja, która robi to samo.
wizzwizz4
1
@ wizzwizz4: str.splitnie uwzględnia ciągłych spacji. re.splitzachowuje spacje. Więc ta funkcja nie zjada żadnych spacji.
dheerozaur
@dheerosaur Myślałem, że "".split()ich nie rozważałem, ale "".split(" ")tak.
wizzwizz4
Twój fragment nie będzie działał poprawnie w title_except('a whim of aN elephant', articles)przypadku sprawy. Możesz użyć word.lower() in exceptionswarunku filtrowania, aby to naprawić.
Dariusz Walczak
@dheerosaur Szukam sposobu na zapisanie wielkimi literami każdego słowa występującego nie tylko po artykule, ale także po numerze. Czy mógłbyś dodać do swojej odpowiedzi dodatek, który to potwierdza? Np. 2001 a Space OdysseyPowinien powrócić 2001 A Space Odyssey, gdzie ajest wielką literą, ponieważ następuje po liczbie. Z góry dziękuję.
ProGrammer
53

Użyj modułu titlecase.py ! Działa tylko w języku angielskim.

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

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

Etienne
źródło
1
Moduł titlecase nie działa, jeśli konwertowany ciąg zawiera dowolną liczbę.
Troy,
1
@Troy Wygląda na to, że problem z liczbą został rozwiązany lub nie trafiłem w twój skrajny przypadek. Np .: sprawa tytułowa („raz 4 dwa”) -> „Jeden 4 dwa”. Teraz titlecase („1one”) -> „1one”, ale „1one ”.title () ->„ 1One ”. chociaż ten późniejszy przypadek jest przypadkiem skrajnym i nie jestem pewien, czy „1One” jest poprawnym tytułem. Nie przejmuję się też na tyle, by złapać książkę do gramatyki.
brent.payne
Nie zadziała w przypadku „321 A BROADWAY STREET”, gdzie dostaję „321 a Broadway Street”. Korzystając z rozwiązania zaproponowanego przez dheerosaura powyżej, powstaje "321 A Broadway Street".
Scratch
Poza tym fajnie, pozostawia nietknięte akronimy w tytule. „Rozwój innowacyjnego TIaSR” staje się „Rozwój innowacyjnego TIaSR”.
Matthias Arras
22

Istnieją następujące metody:

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

Nie ma opcji dla małych liter. Będziesz musiał sam to zakodować, prawdopodobnie używając listy artykułów, które chcesz obniżyć.

nosklo
źródło
titlecase.py małe litery.
TRS-80
14

Stuart Colville stworzył port Pythona dla skryptu Perla napisanego przez Johna Grubera w celu przekształcenia ciągów znaków w tytułowe litery, ale unika stosowania małych słów w oparciu o zasady z podręcznika stylu New York Times, a także zaspokajania kilku specjalnych przypadków.

Niektóre sprytne skrypty:

  • małe słowa, takie jak if, in, of, on , itp. zapisują wielką literą, ale usuwają je z dużej litery, jeśli są błędnie pisane wielką literą w danych wejściowych.

  • skrypty zakładają, że słowa z dużymi literami innymi niż pierwszy znak są już poprawnie pisane wielką literą. Oznacza to, że zostawią samo słowo takie jak „iTunes”, zamiast zamieniać je w „iTunes” lub, co gorsza, „iTunes”.

  • pomijają wszystkie słowa kropkami; „Example.com” i „del.icio.us” pozostaną małymi literami.

  • mają zakodowane hacki przeznaczone specjalnie do obsługi dziwnych przypadków, takich jak „AT&T” i „Q&A”, z których oba zawierają małe słowa (at i a), które normalnie powinny być pisane małymi literami.

  • Pierwsze i ostatnie słowo w tytule jest zawsze pisane wielką literą, więc wpis typu „Nie ma się czego bać” zostanie zamieniony na „Nie ma się czego bać”.

  • Małe słowo po dwukropku będzie pisane wielką literą.

Możesz to pobrać tutaj .

BioGeek
źródło
4
capitalize (word)

To powinno wystarczyć. Rozumiem to inaczej.

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

Ok, jak wspomniano w odpowiedzi powyżej, musisz wprowadzić niestandardową wielkość liter:

mytext = u'i jestem 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)   

To wychodzi

I am a Foobar Bazbar
pyfunc
źródło
Nie tego chcę. Chcę dostać "I am a Foobar Bazbar"
yassin
@Yassin Ezbakhe: Zmieniłem moją odpowiedź, to powinno działać dla Ciebie. Listę artykułów można łatwo pobrać z dowolnego słownika
pyfunc
2

Metoda title w Pythonie 2.7 ma w sobie wadę.

value.title()

powróci cieśli S Assistant gdy wartość Carpenter ' s Asystent

Najlepszym rozwiązaniem jest prawdopodobnie to od @BioGeek przy użyciu titlecase od Stuarta Colville'a. To jest to samo rozwiązanie, które proponuje @Etienne.

boatcoder
źródło
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
"""

Tytuł zaczyna się od słowa pisanego wielką literą, które nie pasuje do artykułu.

Tony Veijalainen
źródło
1

Jednowierszowy wykorzystujący rozumienie list i operator trójskładnikowy

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)

Awaria:

for word in "Wow, a python one liner for titles".split(" ") Dzieli ciąg na listę i inicjuje pętlę for (w interpretacji listy)

word.title() if word not in "the a on in of an" else word używa metody natywnej title() do tytułowania ciągu znaków, jeśli nie jest to artykuł

" ".join łączy elementy listy separatorem (spacja)

user7297223
źródło
0

Jednym z ważnych przypadków, które nie są brane pod uwagę, są akronimy (rozwiązanie python-titlecase może obsłużyć akronimy, jeśli jawnie podasz je jako wyjątki). Zamiast tego wolę po prostu unikać obniżania obudowy. Przy takim podejściu akronimy, które są już dużymi literami, pozostają wielkimi literami. Poniższy kod jest modyfikacją kodu pierwotnie dostarczonego przez dheerosaura.

# 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))

Po uruchomieniu generuje:

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.
August West
źródło