Sustituir tildes en String


#1

Hola,

Llevo varios días intentando sustituir las letras con tildes o
caracteres latinos de una palabra y ya he probado con varias soluciones
que he encontrado por Internet sin éxito.

Lo que quiero es dada por ejemplo la palabra “camión”, transformarla en
“camion”, o “España” por “Espana”. El caso es que con varias soluciones
que he encontrado por Internet consigo que todo funcione correctamente
desde la consola de Ruby, pero luego cuando ejecuto mi aplicación RoR en
Netbeans no funciona. Desde el entorno Netbeans “camión” me retornaría
“camon”, es decir, me trunca los caracteres en vez de sustituirlos.

Aquí os dejo un ejemplo del código que estoy utilizando para hacer esta
conversión. (Hago alguna cosa más como sustituir los espacios en blanco
por ‘-’ y pasarlo todo a minúsculas, pero eso sí funciona)

def nice_slug(str)

accents = {
  ['á','à ','â','ä','ã'] => 'a',
  ['Ã','Ä','Â','À','Á'] => 'A',
  ['é','è','ê','ë'] => 'e',
  ['Ë','É','È','Ê'] => 'E',
  ['í','ì','î','ï'] => 'i',
  ['Í','Î','Ì','Ï'] => 'I',
  ['ó','ò','ô','ö','õ'] => 'o',
  ['Õ','Ö','Ô','Ò','Ó'] => 'O',
  ['ú','ù','û','ü'] => 'u',
  ['Ú','Û','Ù','Ü'] => 'U',
  ['ç'] => 'c', ['Ç'] => 'C',
  ['ñ'] => 'n', ['Ñ'] => 'N'
  }
accents.each do |ac,rep|
  ac.each do |s|
  str = str.gsub(s, rep)
  end
end
str = str.gsub(/[^a-zA-Z0-9 ]/,"")

str = str.gsub(/[ ]+/," ")


str = str.gsub(/ /,"-")

str = str.downcase

end

¿Alguna idea?


#2

Tal vez algo como esto podria ayudar:

“camión”.mb_chars.decompose.scan(/[a-zA-Z0-9]/).join
=> “camion”

Saludos.


#3

Rails añadió el método ‘parameterize’ a la clase String:
“camión”.parameterize
Mira a ver si te sirve

Saludos


#4

Borja Martín wrote:

Rails añadió el método ‘parameterize’ a la clase String:
“camión”.parameterize
Mira a ver si te sirve

Saludos

Ya lo he probado y sigue sin funcionar :(. Si la cadena a convertir no
lleva tilde, funciona correctamente, en cambio, si lo intento con una
cadena con una tilde el error es el siguiente:

undefined method `normalize’ for "Te Contar�n:String

c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/inflector.rb:283:in
transliterate' c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/inflector.rb:262:inparameterize’
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/core_ext/string/inflections.rb:106:in
`parameterize’


#5

Ruben Davila wrote:

Tal vez algo como esto podria ayudar:

“camión”.mb_chars.decompose.scan(/[a-zA-Z0-9]/).join
=> “camion”

Saludos.

Hola,

En efecto, desde la consola de Rails me funciona, pero cuando lanzo la
aplicación y depuro con Netbeans me da error:

undefined method `decompose’ for "La Traves�a:String

¿Dónde está el problema? ¿Por qué en consola sí funciona y cuando
ejecuto la aplicación en Mongrel no?

Un saludo.


#6

2009/4/12 Gunnar W. removed_email_address@domain.invalid:

Umh… Nuevamente -y se darán cuenta que es una óptica que me ha
marcado- creo que lo que tienes que hacer es preguntarte para qué
estás haciendo esto. Si bien hasta hace unos años (antes de que
Unicode fuera aceptado comunmente) sí podías hablar de ventajas de
quitar los diacríticos, reduciendo los caracteres de 8 a 7 bits (esto
es, para caber en el subconjunto US-ASCII), hoy esto ya no tiene
sentido.

Por el código de ejemplo diría que quiere usar algo más que ids en las
URLs con el truco del to_param[1]. Yo creo que en ese caso concreto sí
tiene sentido.

César: he probado el “camión”.parameterize que te proponía Borja en
consola y parece funcionar tanto en rails2.2.2 como en la 2.3.2… (No
lo he probado en Windows, quizá la consola te esté jugando una mala
pasada?

Si no te sirve ese método te recomendaría testear alguno de los
múltiples plugins[2] que realizan esa conversión para ver si te
encajan (recalco lo de testear porque algunos directamente ignoran los
caracteres acentuados y no parece que te conformes con eso). Si
ninguno te encaja o lo quieres usar para otra cosa, puedes ver
cómohacen la conversión examinando su código fuente y aplicarlo en tu
proyecto:

[1] http://www.notsostupid.com/blog/2006/07/07/urls-on-rails/
[2]
http://github.com/search?type=Repositories&language=&q=slug+OR+permalink&repo=&langOverride=&x=0&y=0&start_value=1


#7

Buenas César,

Si en la consola funciona bien seguramente el problema esté en tus
archivos. Comprueba que el encoding es correcto. Rails no se lleva muy
bien con las tildes si el encoding del archivo no es utf8.

P.d. Si este es el problema y los archivos implicados no están en utf8 y
los conviertes, seguramente se te descorromoñen las palabras con tildes,
comiéndose alguna letra pegada a ellas (parecido a lo que te pasa cuando
pruebas desde rails y que no pasa desde consola). Espero que ayude.


#8

¿Has provado ha hacer: ?

Iconv.iconv(‘ascii//ignore//translit’, ‘utf-8’, string).to_s

Es lo que usa internamente rails para hacer la (inflector.rb:277 aprox)

Es lo que usa parameterize.

Y además está encapsulado en un método documentado llamado transliterate
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#M001048

Para llamarla desde consola puedes hacer
ActiveSupport::Inflector.transliterate(string)

A mi me funciona bastante bien, y nunca he tenido ningún problema.

Un Saludo.


#9

Hola,

Después de probar algunas de las soluciones aquí propuestas, he
conseguido el resultado que quería utilizando el código del método
transliterate:

string.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]+/, ‘’)

Respondiendo a Gunnar W. acerca de si realmente tengo necesidad de
realizar esa conversión la respuesta es que necesito construir unas URLs
para extraer datos de determinadas páginas web de terceros a través de
screen scraping, y las URLs de estas páginas no utilizan caracteres
acentuados y demás (obviamente).

Un saludo, y muchas gracias a todos por vuestras respuestas.


#10

César Díaz dijo [Sat, Apr 11, 2009 at 05:52:37PM +0200]:

Hola,

Llevo varios días intentando sustituir las letras con tildes o
caracteres latinos de una palabra y ya he probado con varias soluciones
que he encontrado por Internet sin éxito.

Lo que quiero es dada por ejemplo la palabra “camión”, transformarla en
“camion”, o “España” por “Espana”. El caso es que con varias soluciones
(…)

Umh… Nuevamente -y se darán cuenta que es una óptica que me ha
marcado- creo que lo que tienes que hacer es preguntarte para qué
estás haciendo esto. Si bien hasta hace unos años (antes de que
Unicode fuera aceptado comunmente) sí podías hablar de ventajas de
quitar los diacríticos, reduciendo los caracteres de 8 a 7 bits (esto
es, para caber en el subconjunto US-ASCII), hoy esto ya no tiene
sentido.

Para ahorrarme el escribir lo que mucha gente ha escrito sin duda
mejor que yo respecto a Unicode, te sugiero fuertemente asomarte a la
siguiente liga:

http://www.joelonsoftware.com/articles/Unicode.html
The Absolute Minimum Every Software Developer Absolutely,
Positively Must Know About Unicode and Character Sets (No
Excuses!) (Joel Spolsky)

Y un poquito para poderte refirir a lo que escribo a continuación:

http://inamidst.com/stuff/unidata/
Unicode Codepoint Chart

Una de las peculiaridades que nos impone Unicode es que… ya no
existe una sóla manera de escribir las cosas. Me arriesgo a enviar
aquí caracteres que algunos clientes de correo mal configurados no
mostrarán bien. Pero bueno - ¿Ves alguna diferencia entre las
siguientes letras?

á  á

Son completamente distintas. La primera es un sólo caracter, una a
acentuada (U+00E1). El segundo debe verse igual, pero son dos
caracteres: Una ‘a’ sencilla (U+0061) seguida de un acento agudo
combinante (U+0301).

Una cuestión muy importante de los caracteres combinantes es que
varios de ellos pueden caer sobre de la misma letra:

a̅́́

Esa es una ‘a’ (U+0061) con acento agudo (U+0301), grave (U+0300) y
línea superior (U+0305).

Y claro, hay muchos otros que pueden verse muy similares. Este correo
lo escribo en texto plano (no cambio de tipo de letra ni nada por el
estilo), y si el font con que lees el correo lo soporta, verás algunas
variaciones:

Esto es texto simple

¿Y por qué te insisto en todo esto? Porque para Ruby y para la base de
datos, naturalmente, estas cadenas se van a representar con la
secuencia de caracteres (típicamente con una codificación UTF8)
correspondiente. Incluso si los ves en la consola de Ruby (cabe
mencionar que los puntos UTF normalmente los expresas como U+xxxx,
donde xxxx es un número de 32 bits en representación hexadecimal; lo
que ves en la representación interna de Ruby en este caso es el
Unicode representado como un conjunto de bytes en UTF8, con los bytes
no-imprimibles representados en octales - La primer liga que mencioné
lo explica), te los muestra explicitando su descomposición. Por
ejemplo:

[‘á’, ‘á’, ‘à́̅’, ‘Esto es texto simple’]
=> ["\303\241", “a\314\201”, “a\314\200\314\201\314\205”,
“\357\274\245\357\275\223\357\275\224\357\275\217
\357\275\205\357\275\223
\357\275\224\357\275\205\357\275\230\357\275\224\357\275\217
\357\275\223\357\275\211\357\275\215\357\275\220\357\275\214\357\275\205”]

[‘á’, ‘á’, ‘à́̅’, ‘Esto es texto simple’].map {|str| str.size}
=> [2, 3, 7, 54]

Y, por último, a punto al que iba: ¿Cómo puedes compararlas o
asegurarte que una á es realmente el caracter que creías? Obviamente
los elementos primero y segundo -que se ven idénticos- son diferentes,
dado que su longitud en bytes es distinta. Ahora, si agregamos
caracteres de composición (tomo la à́̅ como ejemplo), ¿qué pasa si
los aplicamos en un órden distinto?

comp = [‘à́̅’, ‘a̅́̀’, ‘á̅̀’]
=> [“a\314\200\314\201\314\205”, “a\314\205\314\201\314\200”,
“\303\241\314\205\314\200”]

comp[0] == comp[1]
=> false

comp[0] == comp[2]
=> false

comp[1] == comp[2]
=> false

Las tres grafías son semánticamente equivalentes, pero su
representación varía dado que el órden en que les puse los acentos es
distinto.

… Y nuevamente, ¿todo esto para qué? Para demostrar que no tiene
sentido intentar encontrar la única representación base, al menos no
con una sencilla tabla de equivalencias. Más bien, habría que
preguntarnos si tiene sentido quitar los diacríticos. ¿Para qué
quieres hacerlo? Si es para facilitar las búsquedas, más bien querrás
usar una biblioteca como Soundex, mucho más elaborada que los esquemas
que han presentado ante tu pregunta.

Saludos,


Gunnar W. - removed_email_address@domain.invalid - (+52-55)5623-0154 / 1451-2244
PGP key 1024D/8BB527AF 2001-10-23
Fingerprint: 0C79 D2D1 2C4E 9CE4 5973 F800 D80E F35A 8BB5 27AF


#11

César Díaz dijo [Mon, Apr 13, 2009 at 05:38:26PM +0200]:

para extraer datos de determinadas páginas web de terceros a través de
screen scraping, y las URLs de estas páginas no utilizan caracteres
acentuados y demás (obviamente).

¿Por qué “obviamente”?

Los URLs pueden contener texto acentuado sin ningún problema. Para un
buen ejemplo, asómate a http://www.tinyarro.ws/ - o, aunque tu cliente
de correo no lo muestre correctamente (o sí, no lo sé), en la
siguiente dirección:

http://✩.ws/➡➨➯➔➞➽➹✩✿❥›⌘‽☁

Saludos,


Gunnar W. - removed_email_address@domain.invalid - (+52-55)5623-0154 / 1451-2244
PGP key 1024D/8BB527AF 2001-10-23
Fingerprint: 0C79 D2D1 2C4E 9CE4 5973 F800 D80E F35A 8BB5 27AF