Проблемы с заголовком в строке Python - PullRequest
2 голосов
/ 04 марта 2009

У меня есть имя в виде строки, в этом примере "markus johansson".

Я пытаюсь закодировать программу, в которой заглавные буквы 'm' и 'j':

name = "markus johansson"

for i in range(1, len(name)):
    if name[0] == 'm':
        name[0] = "M"
    if name[i] == " ":
        count = name[i] + 1
    if count == 'j':    
            name[count] = 'J'  

Я почти уверен, что это должно сработать, но это дает мне такую ​​ошибку:

File "main.py", line 5 in <module> 
   name[0] = "M" 
TypeError: 'str' object does support item assignment 

Я знаю, что есть библиотечная функция с именем .title (), но я хочу заниматься "настоящим программированием".

Как мне это исправить?

Ответы [ 9 ]

9 голосов
/ 04 марта 2009
>>> "markus johansson".title()
'Markus Johansson'

Путь к встроенным строковым методам.

EDIT: Я вижу, вы хотите заново изобрести колесо. Любая конкретная причина? Вы можете выбрать любое количество запутанных методов, таких как:

' '.join(j[0].upper()+j[1:] for j in "markus johansson".split())

Стандартные библиотеки все еще в пути.

9 голосов
/ 04 марта 2009

Я думаю, что вы пытаетесь достичь:

from string import capwords
capwords(name)

Что дает:

'Markus Johansson'

РЕДАКТИРОВАТЬ: ОК, я вижу, вы хотите снести открытую дверь. Вот реализация низкого уровня.

''.join([char.upper() if prev==' ' else char for char,prev in zip(name,' '+name)])
5 голосов
/ 04 марта 2009

string.capwords() (определено в string.py)

# Capitalize the words in a string, e.g. " aBc  dEf " -> "Abc Def".
def capwords(s, sep=None):
    """capwords(s, [sep]) -> string

    Split the argument into words using split, capitalize each
    word using capitalize, and join the capitalized words using
    join. Note that this replaces runs of whitespace characters by
    a single space.

    """
    return (sep or ' ').join(x.capitalize() for x in s.split(sep))

str.title() (определено в stringobject.c)

PyDoc_STRVAR(title__doc__,
"S.title() -> string\n\
\n\
Return a titlecased version of S, i.e. words start with uppercase\n\
characters, all remaining cased characters have lowercase.");
static PyObject*
string_title(PyStringObject *self)
{
    char *s = PyString_AS_STRING(self), *s_new;
    Py_ssize_t i, n = PyString_GET_SIZE(self);
    int previous_is_cased = 0;
    PyObject *newobj = PyString_FromStringAndSize(NULL, n);
    if (newobj == NULL)
        return NULL;
    s_new = PyString_AsString(newobj);
    for (i = 0; i < n; i++) {
        int c = Py_CHARMASK(*s++);
        if (islower(c)) {
            if (!previous_is_cased)
                c = toupper(c);
            previous_is_cased = 1;
        } else if (isupper(c)) {
            if (previous_is_cased)
                c = tolower(c);
            previous_is_cased = 1;
        } else
            previous_is_cased = 0;
        *s_new++ = c;
    }
    return newobj;
}

str.title() в чистом Python

class String(str):
    def title(self):
        s = []
        previous_is_cased = False
        for c in self:
            if c.islower():
               if not previous_is_cased:
                  c = c.upper()
               previous_is_cased = True
            elif c.isupper():
               if previous_is_cased:
                  c = c.lower()
               previous_is_cased = True
            else:
               previous_is_cased = False
            s.append(c)
        return ''.join(s)

Пример:

>>> s = ' aBc  dEf '
>>> import string
>>> string.capwords(s)
'Abc Def'
>>> s.title()
' Abc  Def '
>>> s
' aBc  dEf '
>>> String(s).title()
' Abc  Def '
>>> String(s).title() == s.title()
True
4 голосов
/ 04 марта 2009

Строки неизменны. Они не могут быть изменены. Вы должны создать новую строку с измененным содержимым. Если вы хотите сделать каждый 'j' заглавными буквами:

def make_uppercase_j(char):
    if char == 'j':
        return 'J'
    else:
        return char
name = "markus johansson"
''.join(make_uppercase_j(c) for c in name)
1 голос
/ 04 марта 2009

Если вы ищете более общее решение для имен, вам также следует рассмотреть следующие примеры:

  • Джон Адамс-Смит
  • Жанна д'Арк
  • Жан-Люк де Бреу
  • Донатиен Альфонс Франсуа де Сад

Также некоторые части имен не должны начинаться с заглавных букв, например:

  • Герберт фон Локк
  • Сандер ван Дорн
  • Эдвин ван дер Сад

Итак, если вы хотите создать более общее решение, помните все эти мелочи.

(Это было бы идеальное место для запуска тестовой разработки со всеми этими условиями, которым должен следовать ваш метод / функция) .

1 голос
/ 04 марта 2009

Множество хороших предложений, поэтому я буду в хорошей компании, добавив свои 2 цента: -)

Я предполагаю, что вы хотите что-то более общее, которое может обрабатывать больше, чем просто имена, начинающиеся с 'm' и 'j'. Вы, вероятно, также захотите рассмотреть дефисные имена (например, Маркус Джонсон-Смит), которые также имеют заглавные буквы после дефиса.

from string import lowercase, uppercase
name = 'markus johnson-smith'

state = 0
title_name = []

for c in name:
    if c in lowercase and not state:
        c = uppercase[lowercase.index(c)]
        state = 1
    elif c in [' ', '-']:
        state = 0
    else:
        state = 1 # might already be uppercase

    title_name.append(c)

print ''.join(title_name)

Последнее предостережение - это потенциальная возможность для не-ascii персонажей. В этом случае полезно использовать свойства uppercase и lowercase модуля string, поскольку их содержимое изменяется в зависимости от локали пользователя (т. Е. Зависит от системы или когда вызывается locale.setlocale ()). Я знаю, что вы хотите избежать использования upper() в этом упражнении, и это довольно изящно ... как справка, upper() также использует locale, управляемый setlocale(), поэтому практика использования uppercase и lowercase - хорошее использование API без слишком высокого уровня. Тем не менее, если вам нужно обрабатывать, скажем, французские имена в системе, работающей на английском языке, вам потребуется более надежная реализация.

1 голос
/ 04 марта 2009

Если я правильно понимаю ваш исходный алгоритм, вот что вы хотите сделать:

namn = list("markus johansson")

if namn[0] == 'm':
    namn[0] = "M"

count = 0

for i in range(1, len(namn)):
    if namn[i] == " ":
        count = i + 1
    if count and namn[count] == 'j':    
        namn[count] = 'J'

print ''.join(namn)

Конечно, есть миллион лучших способов («подонков»), чтобы делать то, что вы пытаетесь сделать, как показано в ответе vartec. :)

В нынешнем виде ваш код работает только для имен, которые начинаются с J и M для имени и фамилии соответственно.

0 голосов
/ 09 сентября 2017
string = 'markus johansson'

string = ' '.join(substring[0].upper() + substring[1:] for substring in string.split(' '))

# string == 'Markus Johansson'
0 голосов
/ 04 марта 2009

«Реальное программирование»?

Я бы использовал .title (), и я настоящий программист.

Или я бы использовал регулярные выражения

re.sub(r"(^|\s)[a-z]", lambda m: m.group(0).upper(), "this   is a set of  words")

Здесь написано «Если за началом текста или пробела следует строчная буква» (на английском языке - другие языки, скорее всего, не поддерживаются), то для каждого совпадения преобразуйте текст совпадения в верхний регистр. Поскольку текст совпадения - это пробел и строчная буква, это прекрасно работает.

Если вы хотите использовать его как низкоуровневый код, то работает следующее. Здесь я допускаю только пробел в качестве разделителя (но вы можете захотеть поддерживать перевод строки и другие символы). С другой стороны, «string.lowercase» интернационализирован, поэтому, если вы находитесь в другой локали, он по большей части будет работать. Если вы не хотите этого, используйте string.ascii_lowercase.

import string

def title(s):
    # Capitalize the first character
    if s[:1] in string.lowercase:
        s = s[0].upper() + s[1:]

    # Find spaces
    offset = 0
    while 1:
        offset = s.find(" ", offset)
        # Reached the end of the string or the
        # last character is a space
        if offset == -1 or offset == len(s)-1:
            break

        if s[offset+1:offset+2] in string.lowercase:
            # Is it followed by a lower-case letter?
            s = s[:offset+1] + s[offset+1].upper() + s[offset+2:]
            # Skip the space and the letter
            offset += 2
        else:
            # Nope, so start searching for the next space
            offset += 1

    return s

Чтобы развить мой комментарий к этому ответу, этот вопрос может быть лишь упражнением ради любопытства. Реальные имена имеют специальные правила использования заглавных букв: «van der» в «Johannes Diderik van der Waals» никогда не пишется с заглавной буквы, «Farrah Fawcett-Majors» имеет «M», а «Cathal Ó hEochaidh» использует не-ASCII Ó и h , которые изменяют "Eochaidh", чтобы означать "внук Eochaidh".

...