How to capitalize first letter in strings that may contain numbers

Viewed 2249

I would like to read through a file and capitalize the first letters in a string using Python, but some of the strings may contain numbers first. Specifically the file might look like this:

"hello world"
"11hello world"
"66645world hello"

I would like this to be:

"Hello world"
"11Hello world"
"66645World hello"

I have tried the following, but this only capitalizes if the letter is in the first position.

with open('input.txt') as input, open("output.txt", "a") as output:
    for line in input:
        output.write(line[0:1].upper()+line[1:-1].lower()+"\n")

Any suggestions? :-)

15 Answers

Using regular expressions:

for line in output:
    m = re.search('[a-zA-Z]', line);
    if m is not None:
        index = m.start()
        output.write(line[0:index] + line[index].upper() + line[index + 1:])

You can write a function with a for loop:

x = "hello world"
y = "11hello world"
z = "66645world hello"

def capper(mystr):
    for idx, i in enumerate(mystr):
        if not i.isdigit():  # or if i.isalpha()
            return ''.join(mystr[:idx] + mystr[idx:].capitalize())
    return mystr

print(list(map(capper, (x, y, z))))

['Hello world', '11Hello world', '66645World hello']

You can use regular expression to find the position of the first alphabet and then use upper() on that index to capitalize that character. Something like this should work:

import re

s =  "66645hello world"
m = re.search(r'[a-zA-Z]', s)
index = m.start()

May be worth trying ...

>>> s = '11hello World'
>>> for i, c in enumerate(s):
...     if not c.isdigit():
...         break
... 
>>> s[:i] + s[i:].capitalize()
'11Hello world'

You can find the first alpha character and capitalize it like this:

with open("input.txt") as in_file, open("output.txt", "w") as out_file:
    for line in in_file:
        pos = next((i for i, e in enumerate(line) if e.isalpha()), 0)
        line = line[:pos] + line[pos].upper() + line[pos + 1:]
        out_file.write(line)

Which Outputs:

Hello world
11Hello world
66645World hello

How about this?

import re

text = "1234hello"
index = re.search("[a-zA-Z]", text).start()
text_list = list(text)
text_list[index] = text_list[index].upper()

''.join(text_list)

The result is: 1234Hello

Like this, for example:

import re

re_numstart = re.compile(r'^([0-9]*)(.*)')

def capfirst(s):
    ma = re_numstart.match(s)
    return ma.group(1) + ma.group(2).capitalize()

try this:

with open('input.txt') as input, open("output.txt", "a") as output:
for line in input:
    t_line = ""
    for c in line:
        if c.isalpha():
            t_line += c.capitalize()
            t_line += line[line.index(c)+1:]
            break
        else:
            t_line += c
    output.write(t_line)

Execution result:

Hello world
11Hello world
66645World hello

There is probably a one-line REGEX approach, but using title() should also work:

def capitalise_first_letter(s):
    spl = s.split()
    return spl[0].title() + ' ' + ' '.join(spl[1:])

s = ['123hello world',
"hello world",
"11hello world",
"66645world hello"]


for i in s:
    print(capitalise_first_letter(i))

Producing:

Hello world
11Hello world
66645World hello

You can use regular expression for that:

import re

line = "66645world hello"

regex = re.compile(r'\D')
tofind = regex.search(line)
pos = line.find(tofind.group(0))+1

line = line[0:pos].upper()+line[pos:-pos].lower()+"\n"

print(line)

output: 66645World

Okay, there is already a lot of answers, that should work.

I find them overly complicated or complex though...

Here is a simpler solution:

for s in ("hello world", "11hello world", "66645world hello"):
    first_letter = next(c for c in s if not c.isdigit())
    print(s.replace(first_letter, first_letter.upper(), 1))

The title() method will capitalize the first alpha character of the string, and ignore the digits before it. It also works well for non-ASCII characters, contrary to the regex methods using [a-zA-Z].

From the doc:

str.title()

Return a titlecased version of the string where words start with an uppercase character and the remaining characters are lowercase. [...] The algorithm uses a simple language-independent definition of a word as groups of consecutive letters. The definition works in many contexts but it means that apostrophes in contractions and possessives form word boundaries, which may not be the desired result:

We can take advantage of it this way:

def my_capitalize(s):
    first, rest = s.split(maxsplit=1)
    split_on_quote = first.split("'", maxsplit=1)
    split_on_quote[0] = split_on_quote[0].title()
    first = "'".join(split_on_quote)

    return first + ' ' + rest

A few tests:

tests = ["hello world", "11hello world", "66645world hello", "123ça marche!", "234i'm good"]
for s in tests:
    print(my_capitalize(s))

# Hello world
# 11Hello world
# 66645World hello
# 123Ça marche!  # The non-ASCII ç was turned to uppercase
# 234I'm good    # Words containing a quote are treated properly

With re.sub and repl as a function:

If repl is a function, it is called for every non-overlapping occurrence of pattern. The function takes a single match object argument, and returns the replacement string.

def capitalize(m):
    return m.group(1) + m.group(2).upper() + m.group(3)

lines = ["hello world", "11hello world", "66645world hello"]
for line in lines:
    print re.sub(r'(\d*)(\D)(.*)', capitalize, line)

Output:

Hello world
11Hello world
66645World hello

Using isdigit() and title() for strings:

s = ['123hello world', "hello world", "11hello world", "66645world hello"]
print [each if each[0].isdigit() else each.title() for each in s ]


# ['123hello world', 'Hello World', '11hello world', '66645world hello']                                                                          

If you want to convert the strings starting with a character but not to capitalize the characters after a digit, you can try this piece of code:

def solve(s):
    str1 =""
    for i in s.split(' '):
        str1=str1+str(i.capitalize()+' ') #capitalizes the first character of the string
    return str1

>>solve('hello 5g')
>>Hello 5g
Related