32

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.

sixtyfootersdude
  • 25,859
  • 43
  • 145
  • 213

11 Answers11

42

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.

Matthias
  • 12,873
  • 6
  • 42
  • 48
  • Tried all of the re's on this page. This one had the most compatibility for me. – Druska Jan 23 '13 at 16:29
  • 4
    Looking at my answer one year later: "OMG, I should have added some comments". :) – Matthias Mar 13 '13 at 11:57
  • 4
    This was like a thorn in the flesh. I added the explanation now. – Matthias Nov 12 '14 at 11:52
  • 1
    Love this answer; here's a bit of syntactic sugar that I use for these types of things: convert_camel_case = functools.partial(re.compile(r"...", flags=re.VERBOSE).sub, r' \1') – samwyse Mar 16 '17 at 13:28
  • Just Curious. In my case, I want beginning of string to be upper case. So `simpleBigURL` must return `Simple Big URL`. Can this be tweaked to my need or would it be another regex? – JMD Dec 27 '19 at 22:40
  • Regular expressions have no built-in function to convert characters between uppercase and lowercase. – Matthias Feb 06 '20 at 09:13
30

\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)
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • clever! but does not take care of cases like 'OldHTMLFile' and 'SQLServer', i.e. splitting off abbreviations followed by normal word – Nas Banov Feb 17 '11 at 03:07
  • 2
    @NasBanov that's a much more complex problem that might require spellchecking AI. For instance, `MyJSONAISpec` should be `My JSON AI Spec`, but what regex could figure that out? you either get `My JSONAISpec`, `My JSONAI Spec`, or `MY J S O N A I Spec`. – Ky - Sep 21 '15 at 20:37
  • 2
    @Supuhstar can't expect magic elves here. I posit "My JSONAI Spec" is the expected split of your example. There should been a change of case to flag separate words - presumably you want JSON and AI to be visually separate for humans, so name should be "MyJsonAISpec" or "MyJSONAiSpec" or "MyJsonAiSpec" or use underscores. Let's not make what's unclear to humans be understood by robots. Mathias has posted reasonable solution below – Nas Banov Sep 22 '15 at 16:56
8

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'
Adem Öztaş
  • 20,457
  • 4
  • 34
  • 42
  • 1
    Please consider including some information about your answer, rather than simply posting code. We try to provide not just 'fixes', but help people learn. You should explain what was wrong in the original code, what you did differently, and why your change(s) worked. – Andrew Barber Nov 12 '14 at 14:38
  • 2
    -1. The question **very specifically** asks for something that works with `simpleBigURL` etc. too (i.e. acronyms in all capital letters), something which your solution completely fails at. – Bora M. Alper Jan 20 '21 at 12:27
2

Other method:

def solution(s):
    return ''.join(' ' + c if c.isupper() else c for c in s)
print(solution("mehdHidi"))
mehdi
  • 19
  • 2
1

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)
Prof Mo
  • 483
  • 5
  • 9
0

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

0

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.

Jeff
  • 13,943
  • 11
  • 55
  • 103
  • If it won't work on all upercase, the only delimeter then is a boundry between a lower case letter and an upper case letter. –  Feb 16 '11 at 21:01
0
>>> 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'
vz0
  • 32,345
  • 7
  • 44
  • 77
0

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'
chsws
  • 403
  • 2
  • 6
-1

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

Ky -
  • 30,724
  • 51
  • 192
  • 308
-1

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();
}
Ashu
  • 639
  • 6
  • 11