Python: convert camel case to space delimited using RegEx and taking Acronyms into account

Question:

I am trying to convert camel case to space separated values using python. For example:

divLineColor -> div Line Color

This line does that successfully:

label = re.sub("([A-Z])"," g<0>",label)

The problem I am having is with things like simpleBigURL they should do this:

simpleBigURL -> simple Big URL

I am not entirely sure how to get this result. Help!


This is one thing that I tried:

label = re.sub("([a-z])([A-Z])","g<0> g<1>",label)

But this produces weird results like:

divLineColor -> divL vineC eolor

I was also thinking that using the (?!...) could work but I have not had any luck.

Asked By: sixtyfootersdude

||

Answers:

g<0> references the matched string of the whole pattern while g<1> refereces the matched string of the first subpattern ((…)). So you should use g<1> and g<2> instead:

label = re.sub("([a-z])([A-Z])","g<1> g<2>",label)
Answered By: Gumbo

(?<=[a-z])([A-Z])
or
([a-z])([A-Z])

Answered By: user557597

I couldnt get a really nice regex, but this worked decently.

([a-z]+)([A-Z][a-z]+)?([A-Z][a-z]+)?([A-Z][a-z]+)?([A-Z][a-z]+)?

Breaking it down it is:

([a-z]+) Any series of lowercase characters

([A-Z][a-z]+)? Any uppercase character followed by 1 or more lowercase characters. This is optional

Then I repeated the second group 4 times. This will only work if you dont have any more than 4 “sections” or uppercase characters. Add or take away that regex grouping as necessary. It will work if there less than this number (i.e. it will work on divLineColor) This will not match on words that are all uppercase.

Answered By: Jeff
>>> def unCamel(x): return reduce(lambda a,b: a + ((b.upper() == b and (len(a) and a[-1].upper() != a[-1])) and (' ' + b) or b), x, '')
... 
>>> 
>>> unCamel("simpleBigURL")
'simple Big URL'
>>> unCamel("URL")
'URL'
>>> unCamel("url")
'url'
>>> unCamel("uRl")
'u Rl'
Answered By: vz0

This should work with ‘divLineColor’, ‘simpleBigURL’, ‘OldHTMLFile’ and ‘SQLServer’.

label = re.sub(r'((?<=[a-z])[A-Z]|(?<!A)[A-Z](?=[a-z]))', r' 1', label)

Explanation:

label = re.sub(r"""
        (            # start the group
            # alternative 1
        (?<=[a-z])  # current position is preceded by a lower char
                    # (positive lookbehind: does not consume any char)
        [A-Z]       # an upper char
                    #
        |   # or
            # alternative 2
        (?<!A)     # current position is not at the beginning of the string
                    # (negative lookbehind: does not consume any char)
        [A-Z]       # an upper char
        (?=[a-z])   # matches if next char is a lower char
                    # lookahead assertion: does not consume any char
        )           # end the group""",
    r' 1', label, flags=re.VERBOSE)

If a match is found it is replaced with ' 1', which is a string consisting of a leading blank and the match itself.

Alternative 1 for a match is an upper character, but only if it is preceded by a lower character. We want to translate abYZ to ab YZ and not to ab Y Z.

Alternative 2 for a match is an upper character, but only if it is followed by a lower character and not at the start of the string. We want to translate ABCyz to AB Cyz and not to A B Cyz.

Answered By: Matthias

I know, it’s not regex. But, you can also use map like this

>>> s = 'camelCaseTest'
>>> ''.join(map(lambda x: x if x.islower() else " "+x, s))
'camel Case Test'
Answered By: Adem Öztaş

Here’s my simple solution, which works with PCRE-like implementations, including Python:

/(?<=[a-zA-Z])(?=[A-Z])/g

Then, simply replace all matches with a single space (). Putting it all together:

re.sub(r'(?<=[a-zA-Z])(?=[A-Z])', ' ', yourCamelCaseString);

SSCCE

Answered By: Ky.

I don’t think you can do it using a regular expression because you need to remember the previous element in order to do it for all cases. I think the following function works for all cases. For example, it converts ‘AbcdEfgHIJKlmno’ to ‘Abcd Efg HIJ Klmno’

def camel_case_to_phrase(s):
  prev = None
  t = []
  n = len(s)
  i = 0

  while i < n:
    next_char = s[i+1] if i < n -1 else ''
    c = s[i]
    if prev is None:
        t.append(c)
    elif c.isupper() and prev.isupper():
        if next_char.islower():
            t.append(' ')
            t.append(c)
        else:
            t.append(c)
    elif c.isupper() and not prev.isupper():
        t.append(' ')
        t.append(c)
    else:
        t.append(c)
    prev = c
    i = i +1

return "".join(t)
Answered By: Prof Mo

Hope this method helps :

public static String convertCamelCaseToStatement(String camelCase) {
    StringBuilder builder = new StringBuilder();
    for (Character c : camelCase.toCharArray()) {
        if (Character.isUpperCase(c)) {
            builder.append(" ").append(c);
        } else {
            builder.append(c);
        }
    }
    return builder.toString();
}
Answered By: Ashu

Other method:

def solution(s):
    return ''.join(' ' + c if c.isupper() else c for c in s)
print(solution("mehdHidi"))
Answered By: mehdi

Here is another solution to an old problem. It checks if the next character is upper case and not the characters either side, if so, add a space. The is_upper function handles the None Type produce by the final two characters in the zip_longest function.

from itertools import zip_longest

def is_upper(char):
    try:
        return char.isupper()
    except AttributeError:
        return True

def uncamel(word):
    return ''.join(
        c0 + " " if is_upper(c1) and not (is_upper(c0) and is_upper(c2)) else c0
        for c0, c1, c2 in zip_longest(word, word[1::], word[2::])
        ).strip()

uncamel("simpleBigURLEndsWithWORDIt")
# returns: 'simple Big URL Ends With WORD It'
Answered By: chsws
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.