Estoy tratando de escribir una expresión regular que haga lo siguiente:

  1. _ -> reemplázalo por un espacio
  2. + -> eliminarlo si no hay otro + después de él (es decir, c++ => c++. c+ -> c)
  3. ' -> elimínelo si está al principio o al final de la palabra (es decir, Alin's -> Alin's. 'Alin's -> alin's)
  4. &, -, ., ! - No eliminar.
  5. Otros caracteres especiales: eliminar

Quiero hacerlo pasando una vez la cadena

Por ejemplo:

Input: "abc's, test_s! & c++ c+ 'Dirty's'. and beautiful'..."
Output: "abc's test s! & c++ c Dirty's. and beautiful..."

Explicación:

char `'` in `abc's,` stays because `3`
char `,` in `abc's,` was removed because `5` 
char `_` in `test_s!` was replaced by space because `1`
char `!` in `test_s!` is not removed because `!`
char `&` is not removed because `4`
char `+` in `c++` is not removed because `2`
char `+` in `c+` was removed because `2`
word: `'Dirty's'.` was replaced to `Dirty's.` because `3` and `4`
char `'` in `beautiful'...` was removed because `3`
char `.` is not removed because of `4`

Este es mi código javascript:

var str = "abc's test_s c++ c+ 'Dirty's'. and beautiful";
console.log(str);
str = str.replace(/[_]/g, " ");
str = str.replace(/[^a-zA-Z0-9 &-.!]/g, "");
console.log(str);

Este es mi jsfiddle: http://jsfiddle.net/alonshmiel/LKjYd/4/

No me gusta mi código porque estoy seguro de que es posible hacerlo ejecutándolo una vez sobre la cadena.

¡Cualquier ayuda apreciada!

0
Alon Shmiel 7 feb. 2015 a las 17:08

4 respuestas

La mejor respuesta
function sanitize(str){

  return str.replace(/(_)|(\'\W|\'$)|(^\'|\W\')|(\+\+)|([a-zA-Z0-9\ \&\-\.\!\'])|(.)/g,function(car,p1,p2,p3,p4,p5,p6){

   if(p1) return " "; 
   if(p2) return sanitize(p2.slice(1));
   if(p3) return sanitize(p3.slice(0,-1)); 
   if(p4) return p4.slice(0,p4.length-p4.length%2); 
   if(p5) return car;
   if(p6) return ""; 
 });
}
document.querySelector('#sanitize').addEventListener('click',function(){
  
  document.querySelector('#output').innerHTML=      
	  sanitize(document.querySelector('#inputString').value);
});
#inputString{
  width:290px
}
#sanitize{
  background: #009afd;
  border: 1px solid #1777b7;
  border:none;
  color:#fff;
  cursor:pointer;
  height: 1.55em;
}

#output{
  background:#ddd;
  margin-top:5px;
  width:295px;
}
<input id="inputString" type="text" value="abc's test_s! & c++ c+ 'Dirty's'. and beau)'(tiful'..."/>
<input id="sanitize" type="button" value="Sanitize it!"" />
<div id="output" ></div>

Algunos puntos:

  • la restricción de una pasada no se respeta completamente, debido a la obligación de desinfectar al personaje capturado con \ W. No encuentro otra forma.
  • acerca de la regla ++: cualquier secuencia de + se reduce en uno + si se deteriora.
  • los apóstrofos solo se eliminan si hay un carácter no alfanumérico al lado. ¿Qué debería querer hacer con, por ejemplo: "abc '&". "abc &" o "abc '&"? Y también para "ab_'s".

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter

3
Gaël 17 feb. 2015 a las 16:31

Debido a que el reemplazo que necesita puede ser diferente (nada o un espacio), no puede usar una cadena fija (debido a la restricción de un paso). Entonces, la única forma es usar un reemplazo dinámico.

enfoque directo:

Tratemos de encontrar los caracteres para eliminar y preservar en otros casos los otros:

var str = "abc's, test_s! & c++ c+ 'Dirty's'. and beautiful'...";

var re = /[^\w\s&.!'+-]+|\B'+|'+\B|(\+{2,})|\+|'*(_)'*/g; 

var result = str.replace(re, function (_, g1, g2) {
    if (g1) return g1;
    return (g2) ? ' ' : ''; });

console.log(result);

Cuando se encuentra un guión bajo, se define el grupo de captura 2 (g2 en la función de devolución de llamada) y se devuelve un espacio.

Nota: en el ejemplo anterior, el término "palabra" se toma en un significado regular (la clase de caracteres \w, entonces [a-zA-Z0-9_], excepto el guión bajo), pero si desea ser más riguroso, por ejemplo, excluya comillas simples cerca de dígitos, debe cambiar un poco el patrón:

var re = /[^\w\s&.!'+-]+|(_)'*|([^a-z])'+|'+(?![a-z])|(\+{2,})|\+|^'+/gi;

var result = str.replace(re, function (_, g1, g2, g3) {
    if (g2) return g2;
    if (g3) return g3;
    return (g1) ? ' ' : ''; });

Nota sobre los dos patrones:

Estos dos patrones consisten en una alternancia de 6 o 7 subpatrones que pueden coincidir con 1 o 2 caracteres la mayor parte del tiempo. Ten en cuenta que para encontrar un personaje que eliminar, estos patrones deben probar las 6 o 7 alternativas antes de fallar para cada personaje que no debe ser reemplazado. Es un costo importante y la mayoría de las veces un personaje no necesita ser reemplazado.

Hay una manera de reducir este costo que puede aplicar aquí: la primera discriminación de caracteres

La idea es evitar tanto como sea posible para probar cada subpatrón. Esto se puede hacer aquí porque todos los subpatrones no comienzan con una letra, por lo que puede omitir rápidamente todos los caracteres que son una letra sin tener que probar cada subpatrón, si agrega una mirada anticipada al principio. Ejemplo para el patrón 2:

var re = /(?=[^a-z])(?:[^\w\s&.!'+-]+|(_)'*|([^a-z])'+|'+(?![a-z])|(\+{2,})|\+|^'+)/gi;

Para el primer patrón, puede omitir más caracteres:

var re = /(?=[^a-z0-9\s&.!-])(?:[^\w\s&.!'+-]+|\B'+|'+\B|(\+{2,})|\+|'*(_)'*)/gi;

A pesar de estas mejoras, estos dos patrones necesitan muchos pasos para una cadena pequeña (~ 400) (pero considere que es una cadena de ejemplo con todos los casos posibles) .

un enfoque más indirecto:

Ahora intentemos otra forma que consiste en encontrar un personaje para reemplazar, pero esta vez con todos los personajes anteriores.

var re = /((?:[a-z]+(?:'[a-z]+)*|\+{2,}|[\s&.!-]+)*)(?:(_)|.)?/gi

var result = str.replace(re, function (_, g1, g2) {
    return g1 + ((g2) ? ' ' : '' );
});

(Tenga en cuenta que no hay necesidad de evitar un retroceso catastrófico porque (?:a+|b+|c+)* es seguido por un subpatrón siempre verdadero (?:d|e)?. Además, el patrón completo nunca fallará independientemente de la cadena o la posición en it.

Todos los caracteres antes del carácter para reemplazar (el contenido permitido) son capturados y devueltos por la función de devolución de llamada.

Esta forma necesita más de 2 veces menos pasos para hacer el mismo trabajo.

2
Casimir et Hippolyte 16 feb. 2015 a las 13:18

Lo que necesita es operador de cadena y alternancia.

function customReplace(str){
   return str.replace(/_/g, " ").replace(/^'|'$|[^a-zA-Z0-9 &-.!]|\+(?=[^+])/g,"");
}

La expresión regular /^'|'$|[^a-zA-Z0-9 &-.!]|\+(?=[^+])/g combina todo lo que se necesita para eliminar. Y reemplazamos todos _ por un espacio, que finalmente regresamos.

\+(?=[^+]) busca + seguido de cualquier cosa excepto +

Además, el orden del reemplazo es importante.

1
Amit Joki 7 feb. 2015 a las 14:28

Pruebe esto: por regex /(?!\b)'|'(?=\B)|^'|'$|[^\w\d\s&-.!]|\+(?=[^+])/gm

function sanitize(str) {
  var re = /(?!\b)'|'(?=\B)|^'|'$|[^\w\d\s&-.!]|\+(?=[^+])/gm;
  var subst = '';
  var tmp = str.replace(re, subst);  // remove all condition without (_) 
  var result = tmp.replace("_", " ");  // next replace (_) by ( ) space.
  return result;
}

document.querySelector('#sanitize').addEventListener('click', function() {

  document.querySelector('#output').innerHTML =
    sanitize(document.querySelector('#inputString').value);
});
#inputString {
  width: 290px
}
#sanitize {
  background: #009afd;
  border: 1px solid #1777b7;
  border: none;
  color: #fff;
  cursor: pointer;
  height: 1.55em;
}
#output {
  background: #eee;
  margin-top: 5px;
  width: 295px;
}
<input id="inputString" type="text" value="abc's test_s! & c++ c+ 'Dirty's'. and beau)'(tiful'..." />
<input id="sanitize" type="button" value="Sanitize it!" />
<div id="output"></div>
1
Ahosan Karim Asik 16 feb. 2015 a las 06:53