2008-12-14 17 views
93

Soy nuevo en las expresiones regulares y agradecería su ayuda. Estoy tratando de armar una expresión que dividirá la cadena de ejemplo utilizando todos los espacios que no están rodeados por comillas simples o dobles. Mi último intento se ve así: (?!") y no está funcionando del todo. Se está dividiendo en el espacio antes de la cita.Regex para dividir una cadena usando espacio cuando no está rodeado por comillas simples o dobles

Ejemplo de entrada:

This is a string that "will be" highlighted when your 'regular expression' matches something. 

salida deseada:

This 
is 
a 
string 
that 
will be 
highlighted 
when 
your 
regular expression 
matches 
something. 

Nota que "will be" y 'regular expression' conservan el espacio entre las palabras.

+0

¿En realidad se utiliza el método de "dividir", o si un bucle con el método de "encontrar" en Matcher ser suficiente ? – erickson

+5

"y ahora tiene dos problemas" – hop

Respuesta

206

no lo hago entender por qué todo los otros están proponiendo expresiones regulares tan complejas o código tan largo. Básicamente, desea obtener dos tipos de cosas de su cadena: secuencias de caracteres que no son espacios o comillas, y secuencias de caracteres que comienzan y terminan con una cita, sin comillas intermedias, para dos tipos de comillas. Se puede sincronizar fácilmente esas cosas con esta expresión regular:

[^\s"']+|"([^"]*)"|'([^']*)' 

que añaden los grupos de captura porque no desea que las citas en la lista.

Este código Java construye la lista, agregando el grupo de captura si coincide para excluir las comillas, y agregando la coincidencia total de expresiones regulares si el grupo de captura no coincidió (se hizo coincidir una palabra sin comillas).

List<String> matchList = new ArrayList<String>(); 
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); 
Matcher regexMatcher = regex.matcher(subjectString); 
while (regexMatcher.find()) { 
    if (regexMatcher.group(1) != null) { 
     // Add double-quoted string without the quotes 
     matchList.add(regexMatcher.group(1)); 
    } else if (regexMatcher.group(2) != null) { 
     // Add single-quoted string without the quotes 
     matchList.add(regexMatcher.group(2)); 
    } else { 
     // Add unquoted word 
     matchList.add(regexMatcher.group()); 
    } 
} 

Si no te importa tener las citas en la lista devuelta, se puede utilizar código mucho más simple:

List<String> matchList = new ArrayList<String>(); 
Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); 
Matcher regexMatcher = regex.matcher(subjectString); 
while (regexMatcher.find()) { 
    matchList.add(regexMatcher.group()); 
} 
+1

Ene, gracias por su respuesta. Por cierto, soy un gran fan de EditPad. – carlsz

+4

Sé que es 3 años más tarde, pero esto fue de gran ayuda para mí. – Speck

+0

Funciona muy bien, gracias ! –

1

Probablemente sea más fácil buscar la cadena, agarrando cada parte, dividirla.

Motivo, puede dividirlo en los espacios anteriores y posteriores al "will be". Pero no puedo pensar en ninguna forma de especificar ignorando el espacio entre una división.

(no real de Java)

string = "This is a string that \"will be\" highlighted when your 'regular expression' matches something."; 

regex = "\"(\\\"|(?!\\\").)+\"|[^ ]+"; // search for a quoted or non-spaced group 
final = new Array(); 

while (string.length > 0) { 
    string = string.trim(); 
    if (Regex(regex).test(string)) { 
     final.push(Regex(regex).match(string)[0]); 
     string = string.replace(regex, ""); // progress to next "word" 
    } 
} 

Además, la captura de comillas simples podría conducir a problemas:

"Foo's Bar 'n Grill" 

//=> 

"Foo" 
"s Bar " 
"n" 
"Grill" 
+0

Su solución no maneja cadenas de comillas simples, que son parte del ejemplo de Carl. –

1

String.split() no es útil en este caso porque no hay manera de distinguir entre espacios dentro de comillas (no dividir) y aquellos fuera (dividir). Matcher.lookingAt() es probablemente lo que necesita:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something."; 
str = str + " "; // add trailing space 
int len = str.length(); 
Matcher m = Pattern.compile("((\"[^\"]+?\")|('[^']+?')|([^\\s]+?))\\s++").matcher(str); 

for (int i = 0; i < len; i++) 
{ 
    m.region(i, len); 

    if (m.lookingAt()) 
    { 
     String s = m.group(1); 

     if ((s.startsWith("\"") && s.endsWith("\"")) || 
      (s.startsWith("'") && s.endsWith("'"))) 
     { 
      s = s.substring(1, s.length() - 1); 
     } 

     System.out.println(i + ": \"" + s + "\""); 
     i += (m.group(0).length() - 1); 
    } 
} 

que produce el siguiente resultado:

0: "This" 
5: "is" 
8: "a" 
10: "string" 
17: "that" 
22: "will be" 
32: "highlighted" 
44: "when" 
49: "your" 
54: "regular expression" 
75: "matches" 
83: "something." 
0

Estoy razonablemente seguro de que esto no es posible el uso de expresiones regulares solo. Comprobar si algo está contenido dentro de otra etiqueta es una operación de análisis sintáctico. Esto parece ser el mismo problema que tratar de analizar XML con una expresión regular: no se puede hacer correctamente. Puede obtener el resultado deseado aplicando repetidamente una expresión regular no codiciosa y no global que coincida con las cadenas entrecomilladas, luego, una vez que no puede encontrar nada más, divídala en los espacios ... que tiene una cantidad de problemas, incluido el seguimiento del orden original de todas las subcadenas. Su mejor opción es simplemente escribir una función realmente simple que itere sobre la cadena y extraiga los tokens que desee.

+0

Es posible con una expresión regular, vea algunas de las muestras a las que me he vinculado. Hay algunas variaciones en esto, y he visto varias preguntas similares en SO que abordan esto a través de expresiones regulares. – Jay

+1

Saber cuándo no usar regex es más útil conocimiento para poder crear un (?: (['"]) (. *?) (? \\\\) * \ 1 | ([^ \ s] +)) – Rene

11

Hay varias preguntas sobre StackOverflow que cubren esta misma pregunta en varios contextos utilizando expresiones regulares. Por ejemplo:

ACTUALIZACIÓN: expresión regular de ejemplo para manejar cadenas individuales y dobles comillas.Ref: How can I split on a string except when inside quotes?

m/('.*?'|".*?"|\S+)/g 

probado esto con un fragmento de Perl rápido y la producción fue como se reproduce a continuación. También funciona para cadenas vacías o cadenas solo de espacio en blanco si están entre comillas (no estoy seguro de si eso es deseado o no).

This 
is 
a 
string 
that 
"will be" 
highlighted 
when 
your 
'regular expression' 
matches 
something. 

en cuenta que esto incluya las comillas en los mismos valores coincidentes, aunque se puede extraer que con una cadena de reemplazo, o modificar la expresión regular para no incluirlos. Lo dejo como un ejercicio para el lector u otro póster por ahora, ya que las 2am ya es demasiado tarde para estar jugando con expresiones regulares;)

+0

Creo que su expresión regular permite comillas no coincidentes, por ejemplo, "será" y "expresiones regulares". –

+0

@Zach - tienes razón, lo hace ... lo actualicé para solucionarlo por si acaso – Jay

+0

@Jay Muy bueno Esto funcionó muy bien para mí :) – CalebHC

2
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s 

Esto corresponderá con los espacios no entre comillas dobles. Tengo que usar min, max {0,99999} porque Java no admite * y + en lookbehind.

1

Me gustó el enfoque de Marcus, sin embargo, lo modifiqué para poder permitir texto cerca de las comillas, y admitir los caracteres de comillas "y". Por ejemplo, necesitaba a = "algún valor" para no dividirlo en [ a =, "algún valor"].

(?<!\\G\\S{0,99999}[\"'].{0,99999})\\s|(?<=\\G\\S{0,99999}\".{0,99999}\"\\S{0,99999})\\s|(?<=\\G\\S{0,99999}'.{0,99999}'\\S{0,99999})\\s" 
1

Un par de ajustes de esperar votos en respuesta aceptada de Jan:

(['"])((?:\\\1|.)+?)\1|([^\s"']+) 
  • Permite cotizaciones de escape dentro de cadenas entre comillas
  • Evita repetir el patrón de la comilla simple o doble; Esto también simplifica la adición de más símbolos de cotización, si es necesario (a expensas de un mayor grupo de captura)
3

La expresión regular de Jan Goyvaerts es la mejor solución que encontré hasta ahora, pero también crea vacío (nulo) coincide, que él excluye en su programa. Estas coincidencias vacías también aparecen de probadores de expresiones regulares (por ejemplo, rubular.com). Si activa las búsquedas arround (primero buscar las partes citadas y que las palabras de espacio separed) entonces es posible hacerlo de una vez con: enfoque

("[^"]*"|'[^']*'|[\S]+)+ 
1

de Jan es grande, pero aquí hay otra para el registro.

Si realmente quería dividir como se menciona en el título, manteniendo las cotizaciones en "will be" y 'regular expression', entonces se podría utilizar este método que es directamente de Match (or replace) a pattern except in situations s1, s2, s3 etc

la expresión regular:

'[^']*'|\"[^\"]*\"|() 

Las dos alternancias de la izquierda coinciden con 'quoted strings' y "double-quoted strings". Ignoraremos estos partidos. El lado derecho coincide y captura espacios para el Grupo 1, y sabemos que son los espacios correctos porque no se correspondían con las expresiones de la izquierda. Reemplazamos aquellos con SplitHere y luego los dividimos en SplitHere. Nuevamente, esto es para un verdadero caso dividido donde desea "will be", no will be.

Aquí está una implementación completa de trabajo (ver los resultados en el online demo).

import java.util.*; 
import java.io.*; 
import java.util.regex.*; 
import java.util.List; 

class Program { 
public static void main (String[] args) throws java.lang.Exception { 

String subject = "This is a string that \"will be\" highlighted when your 'regular expression' matches something."; 
Pattern regex = Pattern.compile("\'[^']*'|\"[^\"]*\"|()"); 
Matcher m = regex.matcher(subject); 
StringBuffer b= new StringBuffer(); 
while (m.find()) { 
    if(m.group(1) != null) m.appendReplacement(b, "SplitHere"); 
    else m.appendReplacement(b, m.group(0)); 
} 
m.appendTail(b); 
String replaced = b.toString(); 
String[] splits = replaced.split("SplitHere"); 
for (String split : splits) System.out.println(split); 
} // end main 
} // end Program 
0

También puede probar esto:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something"; 
    String ss[] = str.split("\"|\'"); 
    for (int i = 0; i < ss.length; i++) { 
     if ((i % 2) == 0) {//even 
      String[] part1 = ss[i].split(" "); 
      for (String pp1 : part1) { 
       System.out.println("" + pp1); 
      } 
     } else {//odd 
      System.out.println("" + ss[i]); 
     } 
    } 
+0

realmente se debe añadir alguna explicación sobre por qué debería funcionar esto; también puede agregar código y los comentarios en el código mismo - en su forma actual, no proporciona ninguna explicación que pueda ayudar al resto de la comunidad a comprender lo que le hizo resolver/responder la pregunta. Esto es especialmente importante para las preguntas que ya tienen respuestas. – ishmaelMakitla

0

Si está usando C#, se puede utilizar

string input= "This is a string that \"will be\" highlighted when your 'regular expression' matches <something random>"; 

List<string> list1 = 
       Regex.Matches(input, @"(?<match>\w+)|\""(?<match>[\w\s]*)""|'(?<match>[\w\s]*)'|<(?<match>[\w\s]*)>").Cast<Match>().Select(m => m.Groups["match"].Value).ToList(); 

foreach(var v in list1) 
    Console.WriteLine(v); 

He añadido específicamente "| < ([\ w \? s] *)> "para resaltar que puede especificar cualquier char para agrupar frases. . (En este caso estoy usando <> al grupo

de salida es:

This 
is 
a 
string 
that 
will be 
highlighted 
when 
your 
regular expression 
matches 
something random 
+0

Si tiene una pregunta nueva, hágalo haciendo clic en el botón [Preguntar pregunta] (https://stackoverflow.com/questions/ask). Incluye un enlace a esta pregunta si ayuda a proporcionar contexto. - [De la crítica] (/ review/low-quality-posts/18040810) –

Cuestiones relacionadas