【问题标题】:Problems title-casing a string in Python在 Python 中对字符串进行标题大小写的问题
【发布时间】:2010-10-11 05:22:21
【问题描述】:

我有一个字符串形式的名字,在这个例子中是“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() 的库函数,但我想做“真正的编程”。

我该如何解决这个问题?

【问题讨论】:

  • 我建议你改进标题
  • 真正的程序员使用他们可用的工具,尤其是库。
  • 谁想成为真正的程序员?
  • 真正的程序员有时会对算法的工作原理感兴趣。他的问题完全有道理。

标签: python string uppercase


【解决方案1】:
string = 'markus johansson'

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

# string == 'Markus Johansson'

【讨论】:

    【解决方案2】:

    我猜你想要实现的是:

    from string import capwords
    capwords(name)
    

    产量:

    'Markus Johansson'
    

    编辑:好的,我看到你想拆掉一扇敞开的门。 这是底层实现。

    ''.join([char.upper() if prev==' ' else char for char,prev in zip(name,' '+name)])
    

    【讨论】:

    • 老兄,他想要“真正的编程而不是想成为”。 :)
    • 是的.. 真正的程序员使用整数 ;-)
    • 真正的程序员通过基因工程设计了一群蝴蝶,可以并行写入硬盘。
    • OP 要求没有str.titlestring.capwords 的较低级别实现。
    【解决方案3】:

    如果您正在寻找更通用的名称解决方案,您还应该查看以下示例:

    • 约翰·亚当斯-史密斯
    • 乔安妮 d'Arc
    • 让-吕克·德布鲁
    • Donatien Alphonse François de Sade

    还有一些名称不应该以大写字母开头,例如:

    • 赫伯特·冯·洛克
    • 桑德·范·多恩
    • 埃德温·范德萨德

    所以,如果您正在考虑创建更通用的解决方案,请记住所有这些小事。

    (这将是运行测试驱动开发的理想场所,您的方法/函数必须遵循所有这些条件)

    【讨论】:

    • 这里不是测试驱动开发的地方——这里是审查你的过程并找出为什么名字都是小写的地方,然后改变它,这样你就不会需要事后代码。即使使用事后代码,也要确保任何歧义都由人工解决。
    【解决方案4】:

    “真正的编程”?

    我会使用 .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
    

    为了详细说明我对此答案的评论,出于好奇,这个问题只能作为练习。真实姓名有特殊的大写规则:“Johannes Diderik van der Waals”中的“van der”从不大写,“Farrah Fawcett-Majors”有“M”,“Cathal Ó hEochaidh”使用非ASCII Ó和h ,将“Eochaidh”修改为“Eochaidh 的孙子”。

    【讨论】:

    • 对于长输入(比如 50,000 字或更多),它的性能很差。那是因为您正在为每个单词重新创建字符串。它可能具有比线性大 O 复杂度更差的复杂性。在 StringIO 对象中构建字符串可能会更有效。
    • 顺便说一句,一种为测试生成长输入的简单方法:s = 'test' * 50000
    • 是的,替换次数是二次方。但是 1)这并不意味着作为通用代码 2)OP 想要字符级代码 3)1000 次替换仍然小于 1 毫秒 4)谁真正使用标题?尤其是因为 i18n、"quotes" 等搞砸了,5) 像 "van der Waal" 这样的名字的标题不正确。
    【解决方案5】:

    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
    

    【讨论】:

      【解决方案6】:

      有很多好的建议,所以我会在好公司里加我自己的 2 美分 :-)

      我假设您想要一些更通用的东西,它可以处理的不仅仅是以“m”和“j”开头的名称。您可能还需要考虑在连字符后也有大写字母的连字符名称(如 Markus Johnson-Smith)。

      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 字符的潜力。在这种情况下,使用string 模块的uppercaselowercase 属性是很好的,因为它们的内容会根据用户的区域设置(即:系统相关,或调用locale.setlocale() 时)发生变化。我知道你想避免在这个练习中使用upper(),这非常简洁......作为一个仅供参考,upper() 也使用由setlocale() 控制的locale,所以使用uppercase 和@ 的做法987654330@ 是对 API 的一个很好的使用,而且不会太高级。也就是说,如果您需要在运行英语语言环境的系统上处理法语名称,您将需要一个更强大的实现。

      【讨论】:

      • uppercase[lowercase.index(c)] 使它成为 O(N**2)。使用c.islower()c.lower()等。
      • 完全同意...只是根据他的问题试图帮助他避免使用库函数。
      【解决方案7】:
      >>> "markus johansson".title()
      'Markus Johansson'
      

      内置字符串方法是可行的方法。

      编辑: 我看到你想重新发明轮子。有什么特别的原因吗? 您可以从任意数量的复杂方法中进行选择,例如:

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

      标准库仍然是要走的路。

      【讨论】:

      • 是的,我不知道标题,很棒的提示。又是标准库 rulez。
      • j[1:] -> j[1:].lower() 或只是j.capitalize()str.split() 删除所有空格,因此 ' '.join(...)str.title() 相比做不同的事情。
      • 这是最好最简单的方法。
      【解决方案8】:

      如果我正确理解你的原始算法,这就是你想要做的:

      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 开头的名字和姓氏的名称。

      【讨论】:

        【解决方案9】:

        字符串是不可变的。它们不能改变。您必须使用更改的内容创建一个新字符串。 如果你想让每个 '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)
        

        【讨论】:

        • ''.join('J' if c == 'j' else c for c in name)
        猜你喜欢
        • 2017-08-04
        • 2019-03-30
        • 1970-01-01
        • 2010-11-15
        • 2013-08-18
        相关资源
        最近更新 更多