복붙노트

[RUBY-ON-RAILS] 문자열이 유효한 부동 소수점 값이 있는지 확인

RUBY-ON-RAILS

문자열이 유효한 부동 소수점 값이 있는지 확인

문자열 값이 유효한 부동 소수점 값이 경우 단순히 확인하는 방법이있다. 이 숫자 값이 아닌 경우 문자열에 to_f 호출하면 0.0으로 변환됩니다. 그것은 가까이 내가 원하는 것입니다 잘못된 플로트 문자열을 전달,하지만 난 예외를 잡기 처리하지 않을 때 플로트 ()를 사용하면 예외를 발생시킵니다. 유모와 같은 방법은 내가 정말 원하는 무엇입니까? Float 클래스에 존재를 수행하지만 숫자가 아닌 문자열이 0.0 (사용 to_f)로 변경하지 않고 float로 변환 할 수 없기 때문에 그 도움이되지 않는다.

"a".to_f => 0.0

"a".to_f.nan? => false

Float("a") => ArgumentError: invalid value for Float(): "a"

이것에 대한 간단한 해결책이 있습니까 아니면 문자열이 유효한 부동 소수점 값이 있는지 확인하는 코드를 작성해야합니까?

해결법

  1. ==============================

    1.루비의 세계에 대한 흥미로운 사실은 순수 루비의 대부분은 루비와 표준 라이브러리를 구현하는 Rubinius 프로젝트의 존재이다. 그 결과, 그들은과 같은 커널 # 플로트의 순수 루비 구현이 :

    루비의 세계에 대한 흥미로운 사실은 순수 루비의 대부분은 루비와 표준 라이브러리를 구현하는 Rubinius 프로젝트의 존재이다. 그 결과, 그들은과 같은 커널 # 플로트의 순수 루비 구현이 :

    def Float(obj)
      raise TypeError, "can't convert nil into Float" if obj.nil?
    
      if obj.is_a?(String)
        if obj !~ /^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/
          raise ArgumentError, "invalid value for Float(): #{obj.inspect}"
        end
      end
    
      Type.coerce_to(obj, Float, :to_f)
    end
    

    이것은)가 플로트 (실행될 때 루비가하는 내부 작업과 일치하는 정규 표현식을 제공하지만, 예외없이. 당신이 지금 할 수있는 그래서 :

    class String
      def nan?
        self !~ /^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/
      end
    end
    

    이 솔루션의 좋은 점은 그 Rubinius 실행 이후, 그리고 RubySpec, 당신은 루비 자체 처리하는이 정규식 핸들을 가장자리 케이스를 알고 전달하고 당신은 어떤 두려움없이 문자열에 to_f 호출 할 수 있습니다!

  2. ==============================

    2.여기에 한 가지 방법이다 :

    여기에 한 가지 방법이다 :

    class String
      def valid_float?
        # The double negation turns this into an actual boolean true - if you're 
        # okay with "truthy" values (like 0.0), you can remove it.
        !!Float(self) rescue false
      end
    end
    
    "a".valid_float? #false
    "2.4".valid_float? #true
    

    당신은 문자열의 원숭이 패치를 피하고 싶은 경우에, 당신은 항상 당신은 물론, 제어 일부 모듈의 클래스 메소드 만들 수 있습니다 :

    module MyUtils
      def self.valid_float?(str)
        !!Float(str) rescue false
      end
    end
    MyUtils.valid_float?("a") #false
    
  3. ==============================

    3.

    # Edge Cases:
    # numeric?"Infinity" => true is_numeric?"Infinity" => false
    
    
    def numeric?(object)
    true if Float(object) rescue false
    end
    
    #Possibly faster alternative
    def is_numeric?(i)
    i.to_i.to_s == i || i.to_f.to_s == i
    end
    
  4. ==============================

    4.나는 정규식 대 캐스트 + 예외에 해결되지 않은 토론을보고 난 벤치 마크 모든 것을 시도하고 객관적인 답을 생산할 것이라고 생각 :

    나는 정규식 대 캐스트 + 예외에 해결되지 않은 토론을보고 난 벤치 마크 모든 것을 시도하고 객관적인 답을 생산할 것이라고 생각 :

    다음은 각 방법의 최악의 여기 시도와 최상의 경우의 소스는 다음과 같습니다

    require "benchmark"
    n = 500000
    
    def is_float?(fl)
      !!Float(fl) rescue false
    end
    
    def is_float_reg(fl)
      fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
    end
    
    class String
      def to_float
        Float self rescue (0.0 / 0.0)
      end
    end
    
    
    Benchmark.bm(7) do |x|
      x.report("Using cast best case") {
        n.times do |i|
          temp_fl = "#{i + 0.5}"
          is_float?(temp_fl)
        end
      }
      x.report("Using cast worst case") {
        n.times do |i|
          temp_fl = "asdf#{i + 0.5}"
          is_float?(temp_fl)
        end
      }
      x.report("Using cast2 best case") {
        n.times do |i|
          "#{i + 0.5}".to_float
        end
      }
      x.report("Using cast2 worst case") {
        n.times do |i|
          "asdf#{i + 0.5}".to_float
        end
      }
      x.report("Using regexp short") {
        n.times do |i|
          temp_fl = "#{i + 0.5}"
          is_float_reg(temp_fl)
        end
      }
      x.report("Using regexp long") {
        n.times do |i|
          temp_fl = "12340918234981234#{i + 0.5}"
          is_float_reg(temp_fl)
        end
      }
        x.report("Using regexp short fail") {
        n.times do |i|
          temp_fl = "asdf#{i + 0.5}"
          is_float_reg(temp_fl)
        end
      }
      x.report("Using regexp long fail") {
        n.times do |i|
          temp_fl = "12340918234981234#{i + 0.5}asdf"
          is_float_reg(temp_fl)
        end
      }
    
    end
    

    mri193 다음과 같은 결과 :

    우리는 선형 시간 알고리즘을 처리하고 있기 때문에 나는 우리가 일반화를 만들기 위해 경험적인 측정을 사용할 생각합니다. 그 정규식이 더 일관성을 쉽게 확인할 수와 문자열의 길이에 따라 약간 통과 변동 만합니다. 훨씬 느린 실패가있을 때 거기에는 실패없고, 때 캐스트 빨리 명확하다.

    우리가 성공 시간을 비교하면 우리는 캐스트 최상의 경우는 빠른 정규식 최상의 경우보다 0.3 초에 대한 것을 볼 수 있습니다. 우리의 경우 최악의 경우 시간의 양이 나누면 우리는 심지어 예외 정규식 속도에 맞게 캐스트 아래로 둔화와 함께 휴식을 취할 얼마나 많은 실행 추정 할 수있다. 육초가 0.3로 다이빙에 대해 성능 문제 및 다음 사용 캐스트 + 예외를 실패하는 테스트의 20 미만을 기대한다면 20 그래서에 대한 정보를 제공합니다.

    JRuby를 1.7.4 완전히 다른 결과가 있습니다 :

    캐스트는 소폭 빠르게 최선의 경우 (10 % 약)입니다. 이 차이를 추정하는 것은 (내가 생각하지 않습니다) 일반화하기에 적합하고 휴식도 포인트는 1 예외의 원인이 어딘가에 200과 250 실행 사이입니다.

    정말 예외적 인 상황이 발생하면 예외에만 사용해야합니다 그래서, 이것은 당신과 당신의 코드베이스에 대한 결정이다. 그들은 코드를 사용하지 않을 때 그들은 간단하고 빠르게 할 수 있습니다에 있습니다.

    성능이 중요하지 않습니다 경우 팀 또는 코드베이스가 이미 가지고 있으며,이 모든 답을 무시 어떤 규칙, 당신은 아마 다음과 같은 것이다.

  5. ==============================

    5.음, 당신은 아마도 예외를 원하지 않는 경우 :

    음, 당신은 아마도 예외를 원하지 않는 경우 :

    def is_float?(fl)
       fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
    end
    

    영업 이익 때문에 특별히 예외없이 해결책을 요구했다. 정규 표현식 기반 솔루션은 소폭 느립니다 :

    require "benchmark"
    n = 500000
    
    def is_float?(fl)
      !!Float(fl) rescue false
    end
    
    def is_float_reg(fl)
      fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
    end
    
    Benchmark.bm(7) do |x|
      x.report("Using cast") {
        n.times do |i|
          temp_fl = "#{i + 0.5}"
          is_float?(temp_fl)
        end
      }
      x.report("using regexp") {
        n.times do |i|
          temp_fl = "#{i + 0.5}"
          is_float_reg(temp_fl)
        end
      }
    end
    

    결과 :

    5286 snippets:master!? % 
                 user     system      total        real
    Using cast  3.000000   0.000000   3.000000 (  3.010926)
    using regexp  5.020000   0.000000   5.020000 (  5.021762)
    
  6. ==============================

    6.이 시도

    이 시도

    def is_float(val)
      fval = !!Float(val) rescue false
      # if val is "1.50" for instance
      # we need to lop off the trailing 0(s) with gsub else no match
      return fval && Float(val).to_s == val.to_s.gsub(/0+$/,'') ? true:false
    end 
    
    s = "1000"
    is_float s
     => false 
    
    s = "1.5"
    is_float s
     => true 
    
    s = "Bob"
    is_float s
     => false
    
    n = 1000
    is_float n
     => false 
    
    n = 1.5
    is_float n
     => true
    
  7. ==============================

    7.

    def float?(string)
      true if Float(string) rescue false
    end
    

    이 지지체는 1.5, 5, 123.456, 1_000 아니지만 1000, 1000 등 (예를 들어 동일한 문자열 to_f # 1 등).

    >> float?("1.2")
    => true
    >> float?("1")
    => true
    >> float?("1 000")
    => false
    >> float?("abc")
    => false
    >> float?("1_000")
    => true
    

    출처 : https://github.com/ruby/ruby/blob/trunk/object.c#L2934-L2959

  8. ==============================

    8.나는 주석으로이 문제를 추가하려고하지 않습니다하지만 분명히 댓글 거기에 서식한다 :

    나는 주석으로이 문제를 추가하려고하지 않습니다하지만 분명히 댓글 거기에 서식한다 :

    반면에, 왜처럼 전환 기능으로 그것을 사용하지

    class String
      def to_float
        Float self rescue (0.0 / 0.0)
      end
    end
    "a".to_float.nan? => true
    

    이는 물론 당신이 처음에하고 싶지 않은 것입니다. 나는 대답은, 추측 "당신이 그렇게 왜 당신이, 당신이 정말로 예외 처리를 사용하지 않으려면 자신의 함수를 작성해야하지만?"

  9. ==============================

    9.루비 2.6 플로트하는 새로운 예외 키워드 인수를 추가했다.

    루비 2.6 플로트하는 새로운 예외 키워드 인수를 추가했다.

    문자열이 유효한 플로트가 단순하게있다가 포함되어 있는지 여부 그래서, 지금 확인 :

    Float('22.241234', exception: false)
    # => 22.241234
    
    Float('abcd', exception: false)
    # => nil
    
  10. from https://stackoverflow.com/questions/1034418/determine-if-a-string-is-a-valid-float-value by cc-by-sa and MIT license