Tenga cuidado al usar soluciones basadas en expresiones regulares. Los puntos de referencia muestran que se ejecutan mucho más lentamente que el código normal.
The Tin Man
Respuestas:
135
Puedes usar expresiones regulares. Aquí está la función con las sugerencias de @ janm.
No está mal. En Ruby, generalmente se omite la palabra clave "return" si el valor de retorno se genera en la última expresión de la función. Esto también devolverá un valor entero de cero, probablemente desee un valor booleano, así que algo como !! (str = ~ / ^ [- +]? [0-9] + $ /) haría eso. Luego, podría agregarlo a String y omitir el argumento, usando "self" en lugar de "str", y luego podría cambiar el nombre a "is_i?" ...
enero
2
¡Gracias! No tengo ni idea de las convenciones y prácticas de rubí. Acabo de hacer un rápido google en ruby y expresiones regulares para ver la sintaxis, cambié la expresión regular para aplicar al problema en cuestión y lo probé. Es bastante bueno en realidad ... Puede que tenga que darle una mirada más larga cuando tengo más tiempo libre.
Rado
Tienes la idea correcta, pero no coincide con los literales binarios o hexadecimales (ver mi solución editada a continuación).
Sarah Mei
16
Dos comentarios Puede usar en /regexp/ === selflugar de la !!(self =~ /regexp/)construcción. Puede usar la clase de caracteres '\ d' en lugar de[0-9]
que el
1
La expresión regular más simple para un entero probablemente sea / ^ \ d + $ /
No estoy de acuerdo con las soluciones que provocan una excepción para convertir la cadena: las excepciones no son el flujo de control, y es mejor que lo haga de la manera correcta. Dicho esto, mi solución anterior no trata con enteros que no son de base 10. Así que aquí está la manera de hacerlo sin recurrir a excepciones:
classStringdef integer?[# In descending order of likeliness:/^[-+]?[1-9]([0-9]*)?$/,# decimal/^0[0-7]+$/,# octal/^0x[0-9A-Fa-f]+$/,# hexadecimal/^0b[01]+$/# binary].each do|match_pattern|returntrueifself=~ match_pattern
endreturnfalseendend
Podrías, pero eso sería una mala forma. No utiliza excepciones como flujo de control, y el código de nadie debe contener "rescate falso" (o "rescate verdadero"). Algunos gsub'ing simples harían que mi solución funcione para casos extremos no especificados por el OP.
Sarah Mei
44
Sé que mucha gente lo usa, y ciertamente es estéticamente agradable. Para mí, sin embargo, es una indicación de que el código necesita una reestructuración. Si espera una excepción ... no es una excepción.
Sarah Mei
2
Estoy de acuerdo en que las excepciones no deben usarse como flujo de control. No creo que el requisito sea que se reconozcan los números orientados al desarrollador. En situaciones que no son del programador, esto podría verse como un error, especialmente dado que existe una posible confusión sobre los ceros iniciales y el octal. Tampoco es consistente con to_i. Su código no maneja el caso "-0123". Una vez que maneja ese caso, no necesita una expresión regular separada para octal. Puedes simplemente avanzar usando "any?". La única declaración en su función podría ser "[/ re1 /, / re2 /, / re3 /] .any? {| Re | self = ~ re}", sin cláusulas if o retornos.
Cabe señalar que, si bien esto devuelve verdadero para "01", no lo es para "09", simplemente porque 09no sería un literal entero válido. Si ese no es el comportamiento que desea, puede agregarlo 10como un segundo argumento para Integerque el número siempre se interprete como base 10.
Amigo ... provocando una excepción solo para convertir un número? Las excepciones no son control de flujo.
Sarah Mei
29
No lo son, pero desafortunadamente esta es la forma canónica de determinar el "entero" de una cadena en Ruby. Los métodos que usan #to_iestán demasiado rotos debido a su permisividad.
Sarah: puede usar una expresión regular, pero para manejar todos los casos que Ruby hace al analizar enteros (números negativos, hexadecimales, octales, guiones bajos, por ejemplo, 1_000_000) sería una expresión regular muy grande y fácil de equivocar. Integer()es canónico porque con Integer ()usted sabe con certeza que cualquier cosa que Ruby considere un literal entero será aceptada, y todo lo demás será rechazado. Duplicar lo que el lenguaje ya le brinda es posiblemente un olor a código peor que usar excepciones para el control.
Avdi
9
@Rado So está reinventando la rueda.
sepp2k
24
Puedes hacer un trazador de líneas:
str =...
int =Integer(str)rescuenilif int
int.times {|i| p i}end
o incluso
int =Integer(str)rescuefalse
Dependiendo de lo que intente hacer, también puede usar directamente un bloque de inicio y final con cláusula de rescate:
begin
str =...
i =Integer(str)
i.times do|j|
puts j
endrescueArgumentError
puts "Not an int, doing something else"end
Con respecto al tema "excepción como flujo de control": dado que no sabemos cómo se va a utilizar el método en cuestión, no podemos juzgar si las excepciones encajarían o no. Si se ingresa la cadena y se requiere que sea un número entero, entonces proporcionar un número entero garantizaría una excepción. Aunque entonces tal vez el manejo no está en el mismo método y probablemente solo haríamos Integer (str) .times {| i | pone i} o lo que sea.
Envuélvalo !!o úselo present?si necesita un booleano !!( "12".match /^(\d)+$/ )o "12".match(/^(\d)+$/).present?(este último requiere Rails / soporte activo)
mahemoff
1
Esta expresión regular no tiene en cuenta un signo: los números negativos también son números enteros válidos . Ahora está probando números naturales válidos o cero.
+1 por nombrarlo #integer ?, -1 por desordenar Cadena con él
:-P
1
¿A dónde más iría? integer?("a string")ftl.
August Lilleaas
2
String#integer?es el tipo de parche común que a cada codificador de Ruby y su primo le gusta agregar al lenguaje, lo que lleva a bases de código con tres implementaciones sutilmente incompatibles y una rotura inesperada. Aprendí esto de la manera difícil en grandes proyectos de Ruby.
Avdi
Mismo comentario que el anterior: las excepciones no deben usarse para el flujo de control.
Sarah Mei
Desventaja: esta solución está desperdiciando una conversión.
Robert Klemme
7
La mejor y más simple forma es usar Float
val =Float"234"rescuenilFloat"234"rescuenil#=> 234.0Float"abc"rescuenil#=> nilFloat"234abc"rescuenil#=> nilFloatnilrescuenil#=> nilFloat""rescuenil#=> nil
Integertambién es bueno pero volverá 0porInteger nil
Me alegro de haber notado tu respuesta. De lo contrario, habría ido con "Integer" cuando necesitaba "Float".
Jeff Zivkovic
¡Esto es simple pero la mejor respuesta! Realmente no necesitamos un parche elegante para la clase String en la mayoría de los casos. ¡Esto funciona mejor para mí!
Las respuestas de solo código no son muy útiles. En su lugar, proporcione una explicación de cómo funciona y por qué es la respuesta adecuada. Queremos educar para el futuro para que se entienda la solución, no para resolver la pregunta inmediata.
The Tin Man
3
Personalmente, me gusta el enfoque de excepción, aunque lo haría un poco más conciso:
Para versiones anteriores de Ruby, hay Regexp#===. Y aunque generalmente se debe evitar el uso directo del operador de igualdad de casos, aquí se ve muy limpio:
/^(\d)+$/es una expresión regular para encontrar dígitos en cualquier cadena. Puede probar sus expresiones y resultados de expresiones regulares en http://rubular.com/ .
Lo IntegerRegexguardamos en una constante para evitar la asignación innecesaria de memoria cada vez que lo usamos en el método.
integer?es un método interrogativo que debería devolver trueo false.
matches un método en cadena que coincide con las ocurrencias según la expresión regex dada en el argumento y devuelve los valores coincidentes o nil.
!!convierte el resultado del matchmétodo en booleano equivalente.
Y declarar el método en la Stringclase existente es parchear mono, que no cambia nada en las funcionalidades de String existentes, sino que simplemente agrega otro método nombrado integer?en cualquier objeto String.
¿Podría agregar una pequeña explicación a esto por favor?
stef
@stef - He hecho lo mismo. Avíseme si aún tiene alguna consulta.
Sachin
1
Ampliando la respuesta de @ rado anterior, también se podría usar una declaración ternaria para forzar el retorno de booleanos verdaderos o falsos sin el uso de doble explosión. De acuerdo, la versión de doble negación lógica es más concisa, pero probablemente más difícil de leer para los recién llegados (como yo).
Considere que el uso de expresiones regulares obliga a Ruby a hacer mucho más trabajo, por lo que si se usa en un bucle, ralentizará el código. Anclar la expresión ayuda, pero las expresiones regulares siguen siendo significativamente más lentas.
The Tin Man
1
Para casos más generalizados (incluidos números con punto decimal), puede probar el siguiente método:
No es necesario llamar obj.is_a? Stringporque String # to_s se devolverá, lo que supongo que no requiere demasiado procesamiento en comparación con la .is_a?llamada. De esta manera, solo realizará una llamada en esta línea en lugar de una o dos. Además, podría incluir directamente !!dentro del number?método, porque por convención, ?se supone que un nombre de método que termina devuelve un valor booleano. ¡Saludos!
Esa no es la pregunta original. OP quiere saber si la cadena es un número entero también. por ejemplo "12".is_an_integer? == true"not12".is_an_integer? == false12.is_an_integer? == true
Respuestas:
Puedes usar expresiones regulares. Aquí está la función con las sugerencias de @ janm.
Una versión editada según el comentario de @wich:
En caso de que solo necesite verificar números positivos
fuente
/regexp/ === self
lugar de la!!(self =~ /regexp/)
construcción. Puede usar la clase de caracteres '\ d' en lugar de[0-9]
Bueno, aquí está la manera fácil:
No estoy de acuerdo con las soluciones que provocan una excepción para convertir la cadena: las excepciones no son el flujo de control, y es mejor que lo haga de la manera correcta. Dicho esto, mi solución anterior no trata con enteros que no son de base 10. Así que aquí está la manera de hacerlo sin recurrir a excepciones:
fuente
self.to_i.to_s == self
conInteger self rescue false
?Puede usar
Integer(str)
y ver si aumenta:Cabe señalar que, si bien esto devuelve verdadero para
"01"
, no lo es para"09"
, simplemente porque09
no sería un literal entero válido. Si ese no es el comportamiento que desea, puede agregarlo10
como un segundo argumento paraInteger
que el número siempre se interprete como base 10.fuente
#to_i
están demasiado rotos debido a su permisividad.Integer()
es canónico porque conInteger ()
usted sabe con certeza que cualquier cosa que Ruby considere un literal entero será aceptada, y todo lo demás será rechazado. Duplicar lo que el lenguaje ya le brinda es posiblemente un olor a código peor que usar excepciones para el control.Puedes hacer un trazador de líneas:
o incluso
Dependiendo de lo que intente hacer, también puede usar directamente un bloque de inicio y final con cláusula de rescate:
fuente
fuente
true
yfalse
sinoMatchData
casos ynil
!!
o úselopresent?
si necesita un booleano!!( "12".match /^(\d)+$/ )
o"12".match(/^(\d)+$/).present?
(este último requiere Rails / soporte activo)Ruby 2.6.0 habilita la conversión a un número entero sin generar una excepción , y regresará
nil
si falla la conversión . Y dado que ennil
su mayoría se comporta comofalse
en Ruby, puede verificar fácilmente un número entero como este:fuente
is_
. Me parece tonto en los métodos de interrogación, me gusta"04".integer?
mucho más que"foo".is_integer?
."01"
y tal.fuente
integer?("a string")
ftl.String#integer?
es el tipo de parche común que a cada codificador de Ruby y su primo le gusta agregar al lenguaje, lo que lleva a bases de código con tres implementaciones sutilmente incompatibles y una rotura inesperada. Aprendí esto de la manera difícil en grandes proyectos de Ruby.La mejor y más simple forma es usar
Float
Integer
también es bueno pero volverá0
porInteger nil
fuente
Yo prefiero:
config / initializers / string.rb
y entonces:
o verifica el número de teléfono:
fuente
Una forma mucho más simple podría ser
fuente
fuente
Personalmente, me gusta el enfoque de excepción, aunque lo haría un poco más conciso:
Sin embargo, como ya han dicho otros, esto no funciona con cadenas Octal.
fuente
Ruby 2.4 tiene
Regexp#match?
: (con a?
)Para versiones anteriores de Ruby, hay
Regexp#===
. Y aunque generalmente se debe evitar el uso directo del operador de igualdad de casos, aquí se ve muy limpio:fuente
Esto podría no ser adecuado para todos los casos simplemente usando:
también podría hacer para algunos.
Si es un número y no 0, devolverá un número. Si devuelve 0, es una cadena o 0.
fuente
"12blah".to_i => 12
. Esto podría causar algunos problemas en escenarios extraños.Aquí está mi solución:
Y así es como funciona:
/^(\d)+$/
es una expresión regular para encontrar dígitos en cualquier cadena. Puede probar sus expresiones y resultados de expresiones regulares en http://rubular.com/ .IntegerRegex
guardamos en una constante para evitar la asignación innecesaria de memoria cada vez que lo usamos en el método.integer?
es un método interrogativo que debería devolvertrue
ofalse
.match
es un método en cadena que coincide con las ocurrencias según la expresión regex dada en el argumento y devuelve los valores coincidentes onil
.!!
convierte el resultado delmatch
método en booleano equivalente.String
clase existente es parchear mono, que no cambia nada en las funcionalidades de String existentes, sino que simplemente agrega otro método nombradointeger?
en cualquier objeto String.fuente
Ampliando la respuesta de @ rado anterior, también se podría usar una declaración ternaria para forzar el retorno de booleanos verdaderos o falsos sin el uso de doble explosión. De acuerdo, la versión de doble negación lógica es más concisa, pero probablemente más difícil de leer para los recién llegados (como yo).
fuente
Para casos más generalizados (incluidos números con punto decimal), puede probar el siguiente método:
Puede probar este método en una sesión irb:
Para obtener una explicación detallada de la expresión regular involucrada aquí, consulte este artículo del blog :)
fuente
obj.is_a? String
porque String # to_s se devolverá, lo que supongo que no requiere demasiado procesamiento en comparación con la.is_a?
llamada. De esta manera, solo realizará una llamada en esta línea en lugar de una o dos. Además, podría incluir directamente!!
dentro delnumber?
método, porque por convención,?
se supone que un nombre de método que termina devuelve un valor booleano. ¡Saludos!Me gusta lo siguiente, directo:
Cuidado sin embargo:
fuente
Un trazador de líneas en
string.rb
fuente
No estoy seguro de si esto sucedió cuando se hizo esta pregunta, pero para cualquiera que se encuentre con esta publicación, la forma más simple es:
.is_a?
funcionará con cualquier objeto.fuente
"12".is_an_integer? == true
"not12".is_an_integer? == false
12.is_an_integer? == true