Comment diviser une chaîne CamelCase dans ses sous-chaînes en Ruby?

J'ai une belle CamelCase chaîne telle que ImageWideNice ou ImageNarrowUgly. Maintenant j'ai envie de casser la chaîne dans son sous-chaînes, telles que Image, Wide ou Narrow, et Nice ou Ugly.

Je pensais que cela pourrait être résolu simplement par

camelCaseString =~ /(Image)((Wide)|(Narrow))((Nice)|(Ugly))/

, Mais étrangement, cela ne fera que remplir $1 et $2, mais pas $3.

Avez-vous une meilleure idée pour diviser cette chaîne?

27
demandé sur bastibe 2010-10-22 17:59:21

7 réponses

s = 'nowIsTheTime'

s.split /(?=[A-Z])/

=> ["now", "Is", "The", "Time"]

?=pattern est un exemple de anticipation positif.{[10] } il correspond essentiellement à un point dans la chaîne juste avant le motif . Il ne consomme pas les caractères, c'est-à-dire qu'il n'inclut pas pattern dans le cadre de la correspondance. Un autre exemple:

    irb> 'streets'.sub /t(?=s)/, '-'
=> "stree-s"

Dans ce cas, le {[3] } est mis en correspondance (seul le second t correspond) mais n'est pas remplacé. Merci à @Bryce et à son lien regexp doc. Bryce Anderson ajoute une explication:

Le ?= Au début du groupe de correspondance() est appelé positif lookahead, qui est juste une façon de dire que pendant que l'expression rationnelle est en regardant les personnages pour déterminer si elle correspond, ce n'est pas faisant partie du match. split() mange normalement l'entre-deux caractères, mais dans ce cas, le match lui-même est vide, donc il y a de rien [là].

52
répondu DigitalRoss 2017-05-23 12:02:17

Je sais que c'est vieux, mais mérite d'être mentionné pour d'autres qui pourraient être à la recherche de cela. Dans rails, vous pouvez faire ceci: "NowIsTheTime".underscore.humanize

28
répondu Fitmo Appadmin 2011-11-15 21:27:59

La réponse de DigitalRoss est correcte car elle gère le cas général où vous ne savez pas s'il s'agit d'un cas camel strict (premier caractère en minuscules) ou D'un cas Pascal (première lettre en majuscules).

Si vous savez dans quelle forme se trouve la chaîne, ou si vous voulez forcer l'une ou l'autre, Inflector peut le faire.

Pour le cas Pascal:

"NowIsTheTime".titleize

Pour chameau cas:

"nowIsTheTime".titleize.camelize :lower
8
répondu Tim Scott 2014-08-31 15:47:12

Événement bien que ce soit une question Ruby regex et la réponse par DigitalRoss est correcte et brille par sa simplicité, je veux ajouter une réponse Java:

// this regex doesn't work perfect with Java and other regex engines
"NowIsTheTime".split("(?=[A-Z])"); // ["", "Now", "Is", "The", "Time"]

// this regex works with first uppercase or lowercase characters
"NowIsTheTime".split("(?!(^|[a-z]|$))"); // ["Now", "Is", "The", "Time"]
"nowIsTheTime".split("(?!(^|[a-z]|$))"); // ["now", "Is", "The", "Time"]
2
répondu splash 2017-05-23 12:02:17

Avez-vous essayé

camelCaseString =~ /(Image)(Wide|Narrow)(Nice|Ugly)/

?

2
répondu pjmorse 2012-04-08 00:34:25

La réponse de DigitalRoss ne reconnaîtra pas les acronymes intégrés dans le CamelCase. Par exemple, il divisera "MyHTMLTricks" en "mes astuces H T M L" au lieu de "mes astuces HTML".

Voici une autre option basée sur la fonction AsSpaced() dans PmWiki , qui fait un excellent travail d'être sensible à des cas comme celui-ci:

"MyHTMLTricks" \
.gsub(/([[:lower:]\\d])([[:upper:]])/, '\1 \2') \
.gsub(/([^-\\d])(\\d[-\\d]*( |$))/,'\1 \2') \
.gsub(/([[:upper:]])([[:upper:]][[:lower:]\\d])/, '\1 \2')

=> "My HTML Tricks"

L'autre chose que j'aime dans cette approche est qu'elle laisse la chaîne une chaîne, au lieu de la transformer en un tableau. Si vous avez vraiment voulez-vous le tableau, puis ajoutez simplement une division à la fin.

"MyHTMLTricks" \
.gsub(/([[:lower:]\\d])([[:upper:]])/, '\1 \2') \
.gsub(/([^-\\d])(\\d[-\\d]*( |$))/,'\1 \2') \
.gsub(/([[:upper:]])([[:upper:]][[:lower:]\\d])/, '\1 \2') \
.split

=> ["My", "HTML", "Tricks"]

Pour mémoire, voici le code PHP original de PmWiki.

function AsSpaced($text) {
    $text = preg_replace("/([[:lower:]\\d])([[:upper:]])/", '$1 $2', $text);
    $text = preg_replace('/([^-\\d])(\\d[-\\d]*( |$))/', '$1 $2', $text);
    return preg_replace("/([[:upper:]])([[:upper:]][[:lower:]\\d])/", '$1 $2', $text);
}
0
répondu EFC 2017-12-29 08:34:07
I/p:- "ImageWideNice".scan(/[A-Z][a-z]+/).join(",")

O/p:- "Image,Wide,Nice"
0
répondu Prashant Kajale 2018-09-19 09:19:08