2010-10-22 17 views
27

Tengo una buena cadena CamelCase como ImageWideNice o ImageNarrowUgly. Ahora quiero romper esa cadena en sus subcadenas, como Image, Wide o Narrow, y Nice o Ugly.¿Cómo dividir una cadena CamelCase en sus subcadenas en Ruby?

pensé que esto podría resolverse simplemente

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

Pero, extrañamente, esto sólo se llenará $1 y $2, pero no $3.

¿Tiene una mejor idea para dividir esa cadena?

+1

¿Qué le gustaría hacer con 'ThisIsANarrowImageOfHIV?' Hacer una unión con n, o dividir el VIH? –

Respuesta

50
s = 'nowIsTheTime' 

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

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

?=pattern es un ejemplo de búsqueda positiva hacia delante. En esencia, coincide con un punto en la cadena justo antes del patrón . No consume los caracteres, es decir, no incluye patrón como parte de la coincidencia. Otro ejemplo:

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

En este caso el s se corresponde (sólo la segunda t partidos), pero no sustituye. Gracias a @Bryce y su regexp doc link. Bryce Anderson añade una explicación:

El ?= al comienzo del grupo () partido que se llama búsqueda positiva hacia delante, que es sólo una forma de decir que, si bien la expresión regular es buscando a los personajes para determinar si coincide, no es haciéndolos parte del partido. split() normalmente se come los caracteres intermedios , pero en este caso la coincidencia está vacía, por lo que hay nada [allí].

+1

¿Has probado 'NowIsTheTime'? – splash

+1

@splash: todavía funciona bien – ryeguy

+0

Durante mis pruebas esta expresión regular da como resultado '[" "," Ahora "," Es "," La "," Hora "]' si la primera letra es una letra mayúscula. ¿Qué hago mal? – splash

2

Ha intentado

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

?

2

Evento aunque esto es una cuestión de expresiones regulares Ruby y el answer by DigitalRoss es correcta y brilla por su sencillez, quiero añadir una respuesta de 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"] 
27

Sé que esto es viejo, pero vale la pena mencionar a otros que podría estar buscando esto. En rieles puede hacer esto: "NowIsTheTime".underscore.humanize

5

La respuesta de DigitalRoss es correcta, ya que maneja el caso general en el que no sabe si se trata de camello estricto (minúscula del primer carácter) o caso Pascal (mayúscula de la primera letra).

Si sabe cuál de estas formas es la cadena, o si desea forzar una u otra, Inflector puede hacerlo.

Para el caso de Pascal:

"NowIsTheTime".titleize 

Para el caso de camellos:

"nowIsTheTime".titleize.camelize :lower 
+0

Es importante tener en cuenta que '# titleize' y' # camelize' son estrictamente los métodos de Rails, y no en el núcleo de Ruby. – onebree

0

La respuesta de DigitalRoss no reconocerá siglas incrustados en el CamelCase. Por ejemplo, dividirá "MyHTMLTricks" en "My H T M L Tricks" en lugar de "My HTML Tricks".

Aquí es otra opción basada en la función AsSpaced() en PmWiki, lo que hace un gran trabajo de ser sensible a casos como éste:

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

=> "My HTML Tricks" 

La otra cosa que me gusta de este enfoque es que deja la cadena una cuerda, en lugar de transformarla en una matriz. Si realmente quieres la matriz, simplemente agrega una división al final.

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

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

Para el registro, aquí está el código 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); 
} 
Cuestiones relacionadas