7

The title() method works great, but I have a situation where there are strings that start with both words and numbers and I only want to titlecase the words in the string that do not begin with numbers.

The number of numbers can be variable, and there are not always numbers. Here is an example of each case.

"this is sparta".title() # This Is Sparta

"3rd sparta this is".title() # 3Rd Sparta This Is

"4545numbers start here".title() # "4545Numbers Start Here

I would like these to instead all be changed to:

"This Is Sparta"

"3rd Sparta This Is"

"4545numbers Start Here"

I am using a program that does not allow imports and I need to do this in one line. The only library I can use is re.

My preference would be to use a list comprehension to do this if possible.

Startec
  • 12,496
  • 23
  • 93
  • 160
  • 1
    Wait who down voted this???/ Why???? Why why why – Startec Aug 28 '15 at 23:36
  • Why the no import clause? Standard library imports, such as re, should already be in sys.modules. Try doing import sys; sys.modules to see why. – Alex Huszagh Aug 28 '15 at 23:42
  • I am not sure why. I am using python in an external program, not a custom script, which is why I can't use `import`. When is try to use `string` I get `NameError: "String is not defined"` – Startec Aug 28 '15 at 23:46
  • Surely it should have access to standard libraries though? What interpreter is this? I can understand the need for not loading non-standard libraries, but typically modules like re are loaded on startup. (For example, with Jython, CPython, and PyPy). – Alex Huszagh Aug 28 '15 at 23:51
  • I would have to ask the developers. All the standard modules can be imported, but in a one line parameter, with no import statement I believe that `string` looks like just an undefined variable to the program. – Startec Aug 28 '15 at 23:56
  • Well then the re module, or a one-line, two statement clause should satisfy your needs, or the list comprehension answer given by @mVChr. You could do import re; re.sub... – Alex Huszagh Aug 28 '15 at 23:58
  • What does "I am using python in an external program, not a custom script" mean? The program has to be calling Python *somehow*. You need to be very explicit here because you're doing something way off the wall, and no one is going to understand the restrictions without full details. Telling us what program it is would be a good start. – jpmc26 Aug 29 '15 at 00:33

5 Answers5

16

As it turns out, there's already a function that does this, string.capwords:

>>> import string
>>> string.capwords('1st foo bar bor1ng baz')
'1st Foo Bar Bor1ng Baz'
>>> string.capwords("3rd sparta this is")
'3rd Sparta This Is'

One thing to beware of: runs of whitespace will be collapsed into a single space, and leading and trailing whitespace will be removed. Notably, this means you'll lose line separators. You should split into lines first if you wish to preserve those.

Note that internally, it actually uses the capitalize method instead of title, but this appears to be what you want.

jpmc26
  • 28,463
  • 14
  • 94
  • 146
  • @rcoyner Ironically, I only found it because I was looking up the documentation for `split` for your answer. lol – jpmc26 Aug 28 '15 at 22:45
4

Here's a simple list comprehension:

' '.join([word.capitalize() for word in your_string.split(' ')])

If you want to split on punctuation and other whitespace you'd probably have to use some sort of re function.

jpmc26
  • 28,463
  • 14
  • 94
  • 146
mVChr
  • 49,587
  • 11
  • 107
  • 104
  • I will note that `word[0].upper() + word[1:]` is not quite equivalent to `title`. Example: `'ABCD'[0].upper() + 'ABCD'[1:]` is `'ABCD'`, while `'ABCD'.title()` is `'Abcd'`. I *think* changing `word[1:]` to `word[1:].lower()` would fix it, but at that point, you may as well just `capitalize` instead. – jpmc26 Aug 28 '15 at 22:55
  • Eh, the `string.capwords` solution is best so who cares, right? :) EDIT: *your* `string.capwords` solution :) – mVChr Aug 28 '15 at 22:56
  • Looks like the OP is specifying in edits/comments that for some bizarre reason they're having trouble with the imports, so your approach might actually be better in this case. ;) – jpmc26 Aug 29 '15 at 00:26
  • This is the answer I wanted. I guess I should have specified that I wanted to use a list comprehension. I could not use imports because the expressions are evaluated in Python contexts that are only one line. – Startec Mar 11 '16 at 09:50
3

Regex solution.

In [19]: re.sub(r'\b(\w+)\b', lambda x: x.groups()[0].capitalize(), "3rd sparta this.is1", re.UNICODE)
Out[19]: '3rd Sparta This.Is1'

(see documentation on re.sub)

NightShadeQueen
  • 3,284
  • 3
  • 24
  • 37
1

This could be another option:

  s = "3rd sparta this is"
  " ".join([si.title() if not (str.isdigit(si[0])) else si for si in s.split()])
pafede2
  • 1,626
  • 4
  • 23
  • 40
0

Just set the first character to uppercase

string = string.split (' ')
for x in range (len(string)):
    try:
        string[x] = string[x][0].uppercase() + string [x][1:]
    except ValueError:
        pass
temp = ''
for word in string:
    temp += word + ' '
string = temp
string.title()
SuperNova
  • 138
  • 8