Todas las soluciones tienen problemas al agarrar las malas palabras, si el caso no coincide.La solución de expresiones regulares es más fácil de solucionar mediante la adición de la ignore-caja de la bandera:
badex = /\b(#{bad.split.join('|')})\b/i
Además, el uso de "String".include?(" String ")
dará lugar a problemas de límites con las primeras y últimas palabras en la cadena o cadenas donde las palabras de destino tienen puntuacion o tienen guiones Las pruebas para esas situaciones resultarán en la necesidad de muchos otros códigos. Por eso, creo que la solución de expresiones regulares es la mejor. No es el más rápido, pero va a ser más flexible desde el primer momento y, si los otros algoritmos se ajustan para manejar el plegado de casos y las palabras compuestas, la solución de expresiones regulares podría avanzar.
#!/usr/bin/ruby
require 'benchmark'
bad = 'foo bar baz comparison'
badex = /\b(#{bad.split.join('|')})\b/i
str = "What's the fasted way to check if any word from the bad string is within my comparison string, and what's the fastest way to remove said word if it's found?" * 10
n = 10_000
Benchmark.bm(20) do |x|
x.report('regex:') do
n.times { str.gsub(badex,'').gsub(' ',' ') }
end
x.report('regex with squeeze:') do
n.times{ str.gsub(badex,'').squeeze(' ') }
end
x.report('array subtraction') do
n.times { (str.split(' ') - bad.split(' ')).join(' ') }
end
end
Hice la variable str mucho más tiempo, para hacer que las rutinas trabajen un poco más.
user system total real
regex: 0.740000 0.010000 0.750000 ( 0.752846)
regex with squeeze: 0.570000 0.000000 0.570000 ( 0.581304)
array subtraction 1.430000 0.010000 1.440000 ( 1.449578)
Doh !, estoy demasiado acostumbrado a cómo otros idiomas manejan sus puntos de referencia. ¡Ahora lo tengo funcionando y me veo mejor!
Solo un pequeño comentario sobre lo que parece que el OP está tratando de hacer: la eliminación de palabras en la lista negra es fácil de engañar, y un esfuerzo por mantener. L33t-sp34k hace que sea trivial escuchar las palabras a través de. Dependiendo de la aplicación, la gente lo considerará un juego para encontrar formas de empujar palabras ofensivas después del filtrado. La mejor solución que encontré cuando me pidieron que trabajara en esto, fue crear un generador que creara todas las variaciones en una palabra y las volcara en una base de datos donde algún proceso podría verificar tan pronto como sea posible, en lugar de hacerlo en tiempo real. Se pueden demorar un millón de cadenas pequeñas si estás buscando en una larga lista de palabras ofensivas; Estoy seguro de que podríamos llegar a una lista bastante completa de cosas que alguien consideraría ofensivas, pero eso es un ejercicio para otro día.
No he visto nada similar en Ruby to Perl's Regexp::Assemble, pero esa era una buena forma de resolver este tipo de problema. Puede pasar una serie de palabras, más opciones para plegar mayúsculas y minúsculas, y escupirá un patrón de expresiones regulares que coincidirá con todas las palabras, y se considera que sus características comunes dan como resultado el patrón más pequeño que coincidirá con todas las palabras de la lista. El problema después de eso es ubicar qué palabra en la cadena original coincide con los hits encontrados por el patrón, para que puedan ser eliminados. Las diferencias en palabras en mayúsculas y aciertos dentro de palabras compuestas hacen que el reemplazo sea más interesante.
Y ni siquiera entraremos en palabras que sean benignas u ofensivas según el contexto.
he añadido un bit de prueba más completa del índice de referencia matriz de resta, para adaptarse a la forma en que tendría que trabajar en una verdadera pieza de código. La cláusula if
se especifica en la respuesta, esto ahora lo refleja:
#!/usr/bin/env ruby
require 'benchmark'
bad = 'foo bar baz comparison'
badex = /\b(#{bad.split.join('|')})\b/i
str = "What's the fasted way to check if any word from the bad string is within my comparison string, and what's the fastest way to remove said word if it's found?" * 10
str_split = str.split
bad_split = bad.split
n = 10_000
Benchmark.bm(20) do |x|
x.report('regex') do
n.times { str.gsub(badex,'').gsub(' ',' ') }
end
x.report('regex with squeeze') do
n.times{ str.gsub(badex,'').squeeze(' ') }
end
x.report('bad.any?') do
n.times {
if (bad_split.any? { |bw| str.include?(bw) })
(str_split - bad_split).join(' ')
end
}
end
x.report('array subtraction') do
n.times { (str_split - bad_split).join(' ') }
end
end
con dos pruebas:
ruby test.rb
user system total real
regex 1.000000 0.010000 1.010000 ( 1.001093)
regex with squeeze 0.870000 0.000000 0.870000 ( 0.873224)
bad.any? 1.760000 0.000000 1.760000 ( 1.762195)
array subtraction 1.350000 0.000000 1.350000 ( 1.346043)
ruby test.rb
user system total real
regex 1.000000 0.010000 1.010000 ( 1.004365)
regex with squeeze 0.870000 0.000000 0.870000 ( 0.868525)
bad.any? 1.770000 0.000000 1.770000 ( 1.775567)
array subtraction 1.360000 0.000000 1.360000 ( 1.359100)
Me sale un error de 'expresión regular demasiado grande' con este copypasted. – Arkku
(Por supuesto, esa es otra razón más para ir a la solución hash. =) – Arkku
Tuve una tarea similar hace un par de años, y pasé un par de días buscando listas de palabras y términos ofensivos conocidos. Hubo 479 palabras en inglés consideradas ofensivas después de que lo reduje. Para cada letra de nuestro alfabeto hay al menos dos sustituciones de l33t. Precomputar deletreos alternativos para cada palabra más sus variaciones de l33t me ponen en millones de alternativas. Tratar de hacer coincidir un bloque de texto con eso no iba a ser lo suficientemente rápido para la limpieza en tiempo real. Finalmente logré que mi jefe lo archivara, pero se lo pasó a un desarrollador Jr. que intentó hacerlo sin el l33t. Fácil de superar –