probar - extraer cadenas con expresiones regulares java
Expresiones regulares de Java: reemplace todos los caracteres con `+` excepto las instancias de una cadena dada (7)
Absolutamente solo por el CharBuffer
de hacerlo, una solución que usa CharBuffer
(inesperadamente, tomó mucho más de lo que inicialmente esperaba):
private static String plusOutCharBuffer(String input, String match) {
int size = match.length();
CharBuffer cb = CharBuffer.wrap(input.toCharArray());
CharBuffer word = CharBuffer.wrap(match);
int x = 0;
for (; cb.remaining() > 0;) {
if (!cb.subSequence(0, size < cb.remaining() ? size : cb.remaining()).equals(word)) {
cb.put(x, ''+'');
cb.clear().position(++x);
} else {
cb.clear().position(x = x + size);
}
}
return cb.clear().toString();
}
Tengo el siguiente problema que dice
Reemplace todos los caracteres en una cadena con el símbolo
+
excepto las instancias de la cadena dada en el método
así, por ejemplo, si la cadena dada era abc123efg
y quieren que reemplace todos los caracteres excepto cada instancia de 123
entonces se convertiría en +++123+++
.
Pensé que una expresión regular es probablemente la mejor para esto y se me ocurrió esto.
str.replaceAll("[^str]","+")
donde str es una variable, pero no me permite usar el método sin ponerlo entre comillas. Si solo quiero reemplazar la cadena de cadenas variable, ¿cómo puedo hacer eso? Lo ejecuté con la cadena escrita manualmente y funcionó en el método, pero ¿puedo ingresar una variable?
a partir de ahora creo que está buscando la cadena "str" y no la cadena variable.
Aquí está la salida correcta para tantos casos excepto por dos :(
Lista de casos de prueba abiertos:
plusOut("12xy34", "xy") → "++xy++"
plusOut("12xy34", "1") → "1+++++"
plusOut("12xy34xyabcxy", "xy") → "++xy++xy+++xy"
plusOut("abXYabcXYZ", "ab") → "ab++ab++++"
plusOut("abXYabcXYZ", "abc") → "++++abc+++"
plusOut("abXYabcXYZ", "XY") → "++XY+++XY+"
plusOut("abXYxyzXYZ", "XYZ") → "+++++++XYZ"
plusOut("--++ab", "++") → "++++++"
plusOut("aaxxxxbb", "xx") → "++xxxx++"
plusOut("123123", "3") → "++3++3"
Así que en lugar de encontrar una expresión regular que coincida con la ausencia de una cadena. También podríamos hacer coincidir la frase seleccionada y agregar el número de caracteres omitidos.
StringBuilder sb = new StringBuilder();
Matcher m = Pattern.compile(Pattern.quote(str)).matcher(input);
while (m.find()) {
for (int i = 0; i < m.start(); i++) sb.append(''+'');
sb.append(str);
}
int remaining = input.length() - sb.length();
for (int i = 0; i < remaining; i++) {
sb.append(''+'');
}
El problema en tu solución es que pones un conjunto de cadena de instancia str.replaceAll("[^str]","+")
que excluirá cualquier carácter de la variable str
y que no resolverá tu problema
Por str.replaceAll("[^XYZ]","+")
cuando intente str.replaceAll("[^XYZ]","+")
, se excluirá cualquier combinación de caracteres X
, caracteres Y
y caracteres Z
de su método de reemplazo, por lo que obtendrá " ++XY+++XYZ
".
En realidad, debería excluir una secuencia de caracteres en lugar de str.replaceAll
.
Puede hacerlo utilizando un grupo de captura de caracteres como (XYZ)
luego use un lookahead negativo para hacer coincidir una cadena que no contiene la secuencia de caracteres: ^((?!XYZ).)*$
Verifique esta solution para obtener más información sobre este problema, pero debe saber que puede ser complicado encontrar expresiones regulares para hacerlo directamente.
He encontrado dos soluciones simples para este problema:
Solución 1 :
Puede implementar un método para reemplazar todos los caracteres con '' +
'', excepto la instancia de la cadena dada:
String exWord = "XYZ";
String str = "abXYxyzXYZ";
for(int i = 0; i < str.length(); i++){
// exclude any instance string of exWord from replacing process in str
if(str.substring(i, str.length()).indexOf(exWord) + i == i){
i = i + exWord.length()-1;
}
else{
str = str.substring(0,i) + "+" + str.substring(i+1);//replace each character with ''+'' symbol
}
}
Nota : str.substring(i, str.length()).indexOf(exWord) + i
esta sentencia if excluirá cualquier cadena de instancia de exWord
reemplace el proceso en str
.
Salida :
+++++++XYZ
Solución 2 :
Puedes probar este método utilizando el método ReplaceAll y no necesita ninguna expresión regular compleja:
String exWord = "XYZ";
String str = "abXYxyzXYZ";
str = str.replaceAll(exWord,"*"); // replace instance string with * symbol
str = str.replaceAll("[^*]","+"); // replace all characters with + symbol except *
str = str.replaceAll("//*",exWord); // replace * symbol with instance string
Nota : esta solución solo funcionará si la cadena de entrada no contiene ningún símbolo *
.
También debe escapar de cualquier carácter con un significado especial en una expresión regular en la cadena de instancia de frase exWord
como: exWord = "++"
.
Esto es un poco más complicado de lo que inicialmente podría pensar porque no solo necesita hacer coincidir los caracteres , sino la ausencia de una frase específica : un conjunto de caracteres negado no es suficiente. Si la cadena es 123, necesitarías:
(?<=^|123)(?!123).*?(?=123|$)
https://regex101.com/r/EZWMqM/1/
Es decir: mire por detrás del comienzo de la cadena o "123", asegúrese de que la posición actual no sea seguida por 123, luego repita la lectura de cualquier carácter hasta que la búsqueda anticipada coincida con "123" o el final de la cadena. Esto coincidirá con todos los caracteres que no estén en una subcadena "123". Luego, debe reemplazar cada carácter con un +
, después de lo cual puede usar appendReplacement
y un StringBuffer
para crear la cadena de resultados:
String inputPhrase = "123";
String inputStr = "abc123efg123123hij";
StringBuffer resultString = new StringBuffer();
Pattern regex = Pattern.compile("(?<=^|" + inputPhrase + ")(?!" + inputPhrase + ").*?(?=" + inputPhrase + "|$)");
Matcher m = regex.matcher(inputStr);
while (m.find()) {
String replacement = m.group(0).replaceAll(".", "+");
m.appendReplacement(resultString, replacement);
}
m.appendTail(resultString);
System.out.println(resultString.toString());
Salida:
+++123+++123123+++
Tenga en cuenta que si inputPhrase
puede contener caracteres con un significado especial en una expresión regular, primero tendrá que escapar de ellos antes de concatenar en el patrón.
Para hacer este trabajo necesitas una bestia de un patrón. Digamos que usted está operando en el siguiente caso de prueba como un ejemplo:
plusOut("abXYxyzXYZ", "XYZ") → "+++++++XYZ"
Lo que debe hacer es crear una serie de cláusulas en su patrón para que coincidan con un solo carácter a la vez:
- Cualquier carácter que NO sea "X", "Y" o "Z" -
[^XYZ]
- Cualquier "X" no seguida de "YZ" -
X(?!YZ)
- Cualquier "Y" no precedida por "X" -
(?<!X)Y
- Cualquier "Y" no seguida de "Z" -
Y(?!Z)
- Cualquier "Z" no precedida por "XY" -
(?<!XY)Z
Un ejemplo de este reemplazo se puede encontrar aquí: https://regex101.com/r/jK5wU3/4
Aquí hay un ejemplo de cómo podría funcionar esto (sin duda no está optimizado, pero funciona):
import java.util.regex.Pattern;
public class Test {
public static void plusOut(String text, String exclude) {
StringBuilder pattern = new StringBuilder("");
for (int i=0; i<exclude.length(); i++) {
Character target = exclude.charAt(i);
String prefix = (i > 0) ? exclude.substring(0, i) : "";
String postfix = (i < exclude.length() - 1) ? exclude.substring(i+1) : "";
// add the look-behind (?<!X)Y
if (!prefix.isEmpty()) {
pattern.append("(?<!").append(Pattern.quote(prefix)).append(")")
.append(Pattern.quote(target.toString())).append("|");
}
// add the look-ahead X(?!YZ)
if (!postfix.isEmpty()) {
pattern.append(Pattern.quote(target.toString()))
.append("(?!").append(Pattern.quote(postfix)).append(")|");
}
}
// add in the other character exclusion
pattern.append("[^" + Pattern.quote(exclude) + "]");
System.out.println(text.replaceAll(pattern.toString(), "+"));
}
public static void main(String [] args) {
plusOut("12xy34", "xy");
plusOut("12xy34", "1");
plusOut("12xy34xyabcxy", "xy");
plusOut("abXYabcXYZ", "ab");
plusOut("abXYabcXYZ", "abc");
plusOut("abXYabcXYZ", "XY");
plusOut("abXYxyzXYZ", "XYZ");
plusOut("--++ab", "++");
plusOut("aaxxxxbb", "xx");
plusOut("123123", "3");
}
}
ACTUALIZACIÓN: Incluso esto no funciona del todo porque no puede lidiar con exclusiones que son solo caracteres repetidos, como "xx". Las expresiones regulares definitivamente no son la herramienta adecuada para esto, pero pensé que podría ser posible. Después de hurgar, no estoy tan seguro de que exista un patrón que pueda hacer que esto funcione.
Parece que este es el problema de plusOut en CodingBat.
Tenía 3 soluciones para este problema y escribí una nueva solución de transmisión solo por diversión.
Solución 1: Bucle y cheque.
Cree un StringBuilder fuera de la cadena de entrada y verifique la palabra en cada posición. Reemplace el carácter si no coincide, y omita la longitud de la palabra si la encuentra.
public String plusOut(String str, String word) {
StringBuilder out = new StringBuilder(str);
for (int i = 0; i < out.length(); ) {
if (!str.startsWith(word, i))
out.setCharAt(i++, ''+'');
else
i += word.length();
}
return out.toString();
}
Esta es probablemente la respuesta esperada para un programador principiante, aunque se supone que la cadena no contiene ningún carácter de plano astral, que estaría representado por 2 caracteres en lugar de 1.
Solución 2: Reemplace la palabra con un marcador, reemplace el resto, luego restaure la palabra
public String plusOut(String str, String word) {
return str.replaceAll(java.util.regex.Pattern.quote(word), "@").replaceAll("[^@]", "+").replaceAll("@", word);
}
No es una solución adecuada ya que asume que un determinado carácter o secuencia de caracteres no aparece en la cadena.
Tenga en cuenta el uso de Pattern.quote
para evitar que la word
se interprete como sintaxis de replaceAll
regulares por el método replaceAll
.
Solución 3: Regex con /G
public String plusOut(String str, String word) {
word = java.util.regex.Pattern.quote(word);
return str.replaceAll("//G((?:" + word + ")*+).", "$1+");
}
Construir regex /G((?:word)*+).
, que hace más o menos lo que hace la solución 1:
-
/G
se asegura de que el partido comience desde donde termina el partido anterior -
((?:word)*+)
selecciona 0 o más instancias deword
, si las hay, para que podamos mantenerlas en el reemplazo con$1
. La clave aquí es el cuantificador posesivo*+
, que obliga a la expresión regular a mantener cualquier instancia de laword
que encuentre. De lo contrario, la expresión regular no funcionará correctamente cuando aparezca laword
al final de la cadena, ya que la expresión regular retrocede para coincidir.
-
.
no formará parte de ningunaword
, ya que la parte anterior ya selecciona todas las apariciones consecutivas deword
y no permite retroceder. Reemplazaremos esto con+
Solución 4: Streaming
public String plusOut(String str, String word) {
return String.join(word,
Arrays.stream(str.split(java.util.regex.Pattern.quote(word), -1))
.map((String s) -> s.replaceAll("(?s:.)", "+"))
.collect(Collectors.toList()));
}
La idea es dividir la cadena por word
, hacer el reemplazo en el resto, y unirlos con la word
usando el método String.join
.
- Igual que el anterior, necesitamos
Pattern.quote
para evitarsplit
interpretación de laword
como expresiones regulares. Dado que lasplit
por defecto elimina la cadena vacía al final de la matriz, necesitamos usar-1
en el segundo parámetro para hacer que lasplit
deje esas cadenas vacías en paz. - Luego creamos un flujo fuera de la matriz y reemplazamos el resto como cadenas de
+
. En Java 11, podemos usars -> String.repeat(s.length())
lugar. - El resto es simplemente convertir el flujo a un iterable (lista en este caso) y unirlos para obtener el resultado.
Puedes hacerlo en una línea:
input = input.replaceAll("((?:" + str + ")+)?(?!" + str + ").((?:" + str + ")+)?", "$1+$2");
Esto captura opcionalmente "123" a cada lado de cada personaje y los coloca de nuevo (un espacio en blanco si no hay "123"):