2008-10-13 5 views
14

J'ai un fichier dont le format est modifié par un script python. J'ai plusieurs chaînes camel dans ce fichier où je veux juste insérer un seul espace avant la lettre majuscule - ainsi "WordWordWord" devient "Word Word Word". Mon expérience d'expression rationnelle limitée s'est arrêtée sur moi - quelqu'un peut-il penser à une regex décente pour le faire, ou (mieux encore) y at-il une façon plus pythonique de faire cela que je manque?Je cherche une façon pythonique d'insérer un espace avant les lettres majuscules

Répondre

23

Vous pouvez essayer:

>>> re.sub(r"(\w)([A-Z])", r"\1 \2", "WordWordWord") 
'Word Word Word' 
+1

re.sub (r "(\ w) ([AZ])", r "\ 1 \ 2", "SorryIThinkYouMissedASpot") – tzot

+0

Comme petite amélioration, [[: upper:]] devrait être utilisé à la place de [AZ]. – Tomalak

+4

@Tomalak, '[[: upper:]]' n'est pas supporté par Python. C'est une [expression de support POSIX] (http://www.regular-expressions.info/posixbrackets.html). –

3

Avec regexes vous pouvez faire ceci:

re.sub('([A-Z])', r' \1', str) 

Bien sûr, cela ne fonctionne que pour les caractères ASCII, si vous voulez faire Unicode c'est un tout nouveau boîte de vers :-)

+1

re.sub ('([A-Z])', r '\ 1', "Voulons-nous un espace avant les D de cette phrase?") – tzot

+0

Ah, oui, bon point. On dirait que les solutions de Leonhard et de Leonhard sont correctes. –

24

S'il y a des majuscules consécutives, alors le résultat de Greg pourrait ne pas être ce que vous cherchez, puisque le \ w consomme le caractère devant la lettre capitale à remplacer.

>>> re.sub(r"(\w)([A-Z])", r"\1 \2", "WordWordWWWWWWWord") 
'Word Word WW WW WW Word' 

Un regard-behind résoudrait ceci:

>>> re.sub(r"(?<=\w)([A-Z])", r" \1", "WordWordWWWWWWWord") 
'Word Word W W W W W W Word' 
+0

La réponse de Dan est meilleure et plus simple :) – hayalci

+0

@hayalci: re.sub ('([A-Z])', r '\ 1', 'Vraiment?') – tzot

8

Jetez un oeil à ma réponse sur .NET - How can you split a “caps” delimited string into an array?

Edit: Peut-être mieux l'inclure ici.

re.sub(r'([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))', r'\1 ', text) 

Par exemple:

"SimpleHTTPServer" => ["Simple", "HTTP", "Server"] 
+0

Votre réponse est probablement ce que veut réellement Electrons_Ahoy; Cependant, selon le libellé de leur question, ce n'est pas le cas. – tzot

+0

mais merci de partager celui-ci, c'est une réponse géniale! –

10

Peut-être plus court:

>>> re.sub(r"\B([A-Z])", r" \1", "DoIThinkThisIsABetterAnswer?") 
+1

Pour quiconque se demandant, '\ B' est" Pas de limite de mot ". Donc, ce n'est pas insérer des espaces où il y a déjà un espace. – ArtOfWarfare

0

Je suis d'accord que la solution de regex est le plus facile, mais je ne dirais pas que c'est le plus pythonique.

Que diriez-vous:

text = 'WordWordWord' 
new_text = '' 

for i, letter in enumerate(text): 
    if i and letter.isupper(): 
     new_text += ' ' 

    new_text += letter 
+0

Cela a le même problème que Dan - vous obtiendrez des espaces supplémentaires avant les majuscules, même si elles ne sont pas nécessaires. – Brian

+0

Certes, je l'ai édité pour ajouter un drapeau ... J'avoue que c'est un peu lourd, mais peut être plus facile à retenir que regex. – monkut

0

Je pense que regexes sont le chemin à parcourir, mais juste pour donner une version pure python sans (je l'espère) l'un des problèmes ΤΖΩΤΖΙΟΥ a souligné:

def splitCaps(s): 
    result = [] 
    for ch, next in window(s+" ", 2): 
     result.append(ch) 
     if next.isupper() and not ch.isspace(): 
      result.append(' ') 
    return ''.join(result) 
fenêtre

() est une fonction d'utilité j'utilise pour fonctionner sur une fenêtre glissante d'éléments, définis comme:

import collections, itertools 

def window(it, winsize, step=1): 
    it=iter(it) # Ensure we have an iterator 
    l=collections.deque(itertools.islice(it, winsize)) 
    while 1: # Continue till StopIteration gets raised. 
     yield tuple(l) 
     for i in range(step): 
      l.append(it.next()) 
      l.popleft() 
2

Peut-être que vous seriez intéressé par la mise en œuvre d'un revêtement sans utiliser regexp:

''.join(' ' + char if char.isupper() else char.strip() for char in text).strip() 
1

Si vous avez des acronymes, vous ne voulez probablement pas d'espace entre eux.Ce regex deux étapes gardera intact acronymes (et traiter également des signes de ponctuation et d'autres lettres non en majuscules comme quelque chose à ajouter un espace):

re_outer = re.compile(r'([^A-Z ])([A-Z])') 
re_inner = re.compile(r'(?<!^)([A-Z])([^A-Z])') 
re_outer.sub(r'\1 \2', re_inner.sub(r' \1\2', 'DaveIsAFKRightNow!Cool')) 

La sortie sera: 'Dave Is AFK Right Now! Cool'

Questions connexes