如何解析 ruby BigDecimal inspect?
How to parse ruby BigDecimal inspect?
在下面的代码中:
x = BigDecimal(10)
s = x.inspect # "#<BigDecimal:6fe4790,'0.1E2',9(36)>"
有没有办法解析 s
并获得原始值?原因是我有一些使用 inspect 在其中写入 BigDecimal 的文本文件,我需要解析这些值。
你.to_s
获取字符串中的值。 .inspect
将打印对象
x = BigDecimal(10)
x.to_s
# => "0.1E2"
"#<BigDecimal:6fe4790,'0.1E2',9(36)>"[/(?<=').+(?=')/]
# => "0.1E2"
我不知道你用的是哪个版本的Ruby,所以我检查了some MRI source code for BigDecimal:
2000 /* Returns debugging information about the value as a string of comma-separated
2001 * values in angle brackets with a leading #:
2002 *
2003 * BigDecimal.new("1234.5678").inspect ->
2004 * "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
2005 *
2006 * The first part is the address, the second is the value as a string, and
2007 * the final part ss(mm) is the current number of significant digits and the
2008 * maximum number of significant digits, respectively.
2009 */
2010 static VALUE
2011 BigDecimal_inspect(VALUE self)
2012 {
2013 ENTER(5);
2014 Real *vp;
2015 volatile VALUE obj;
2016 size_t nc;
2017 char *psz, *tmp;
2018
2019 GUARD_OBJ(vp, GetVpValue(self, 1));
2020 nc = VpNumOfChars(vp, "E");
2021 nc += (nc + 9) / 10;
2022
2023 obj = rb_str_new(0, nc+256);
2024 psz = RSTRING_PTR(obj);
2025 sprintf(psz, "#<BigDecimal:%"PRIxVALUE",'", self);
2026 tmp = psz + strlen(psz);
2027 VpToString(vp, tmp, 10, 0);
2028 tmp += strlen(tmp);
2029 sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
2030 rb_str_resize(obj, strlen(psz));
2031 return obj;
2032 }
2033
所以,您想要的似乎是检查字符串的第二部分,在您的情况下 0.1E2
等于 10
。上面的注释已经很清楚了,这应该是对象的完整数值。简单的正则表达式就足够了。
另一个选项:
"#<BigDecimal:95915c4,'0.1E2',9(27)>".split(",")[1].tr! "'", ''
=> "0.1E2"
BigDecimal#inspect 的文档不完整。考虑以下因素:
require 'bigdecimal`
BigDecimal.new("1.2345").inspect
#=> "#<BigDecimal:7fb06a110298,'0.12345E1',18(18)>"
...
BigDecimal.new("1.234567890").inspect
#=> "#<BigDecimal:7fb06a16ab58,'0.123456789E1',18(27)>"
BigDecimal.new("1.2345678901").inspect
#=> "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>"
BigDecimal.new("1.23456789012").inspect
#=> "#<BigDecimal:7fb06a1393a0,'0.1234567890 12E1',27(27)>"
BigDecimal.new("1.234567890123").inspect
#=> "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
从inspect
的源码可以看出,如果有效数字超过10位,则每10个字符之间用一个space隔开(为了便于阅读,大概):
BigDecimal.new("123.456789012345678901234567").inspect
#=> "#<BigDecimal:7fb06a0ac8b0,'0.1234567890 1234567890 1234567E3',36(36)>"
我建议按如下方式检索 BigDecimal
值的字符串表示形式:
str = "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>"
str.delete(' ').split(?')[1]
#=> "0.12345678901E1"
我们还没有完成。我们仍然必须将提取的字符串转换为数字对象。我们不能使用BigDecimal#to_f,但是,如果该值的绝对值很大:
"1.23456789012345678".to_f
#=> 1.2345678901234567
最安全的做法是 return 一个 BigDecimal
对象,使用方法 BigDecimal::new,它有两个参数:
要转换为 BigDecimal
对象的值,可以是 Integer、Float、Rational、BigDecimal 或 String。如果是我们将提供的字符串,"spaces are ignored and unrecognized characters terminate the value"(类似于 "123.4cat".to_f #=> 123.4
)。
有效位数。如果省略或为零,则有效位数由值确定。我将省略这个论点。 (例如,BigDecimal.new("0.1234E2").precs #=> [18, 18]
,其中数组包含当前和最大有效数字位数。
请注意,如果第一个参数是 Float 或 Rational,则第二个参数是必需的,否则它是可选的。
因此我们可以写成:
require 'bigdecimal'
def convert(str)
BigDecimal.new(str.delete(' ').split(?')[1])
end
convert "#<BigDecimal:7facd39d7ee8,'0.1234E4',9(18)>"
#=> #<BigDecimal:7facd39c7de0,'0.1234E4',9(18)>
convert "#<BigDecimal:7facd39b7be8,'0.1234E2',18(18)>"
#=> #<BigDecimal:7facd39ae610,'0.1234E2',18(18)>
convert "#<BigDecimal:7facd3990638,'0.1234E0',9(18)>"
#=> #<BigDecimal:7facd3980aa8,'0.1234E0',9(18)>
convert "#<BigDecimal:7facd3970e28,'0.1234E-2',9(18)>"
#=> #<BigDecimal:7facd39625d0,'0.1234E-2',9(18)>
v = convert "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
#=> #<BigDecimal:7fb069851d78,'0.1234567890 123E1',27(27)>
查看 BigDecimal
对象是否可以在不损失准确性的情况下转换为浮点数的简单方法是:
def convert_bd_to_float(bd)
f = bd.to_f
(bd==BigDecimal.new(f.to_s)) ? f : nil
end
convert_bd_to_float BigDecimal.new('1234567890123456')
#=> 1.234567890123456e+15
convert_bd_to_float BigDecimal.new('12345678901234567')
#=> nil
在下面的代码中:
x = BigDecimal(10)
s = x.inspect # "#<BigDecimal:6fe4790,'0.1E2',9(36)>"
有没有办法解析 s
并获得原始值?原因是我有一些使用 inspect 在其中写入 BigDecimal 的文本文件,我需要解析这些值。
你.to_s
获取字符串中的值。 .inspect
将打印对象
x = BigDecimal(10)
x.to_s
# => "0.1E2"
"#<BigDecimal:6fe4790,'0.1E2',9(36)>"[/(?<=').+(?=')/]
# => "0.1E2"
我不知道你用的是哪个版本的Ruby,所以我检查了some MRI source code for BigDecimal:
2000 /* Returns debugging information about the value as a string of comma-separated
2001 * values in angle brackets with a leading #:
2002 *
2003 * BigDecimal.new("1234.5678").inspect ->
2004 * "#<BigDecimal:b7ea1130,'0.12345678E4',8(12)>"
2005 *
2006 * The first part is the address, the second is the value as a string, and
2007 * the final part ss(mm) is the current number of significant digits and the
2008 * maximum number of significant digits, respectively.
2009 */
2010 static VALUE
2011 BigDecimal_inspect(VALUE self)
2012 {
2013 ENTER(5);
2014 Real *vp;
2015 volatile VALUE obj;
2016 size_t nc;
2017 char *psz, *tmp;
2018
2019 GUARD_OBJ(vp, GetVpValue(self, 1));
2020 nc = VpNumOfChars(vp, "E");
2021 nc += (nc + 9) / 10;
2022
2023 obj = rb_str_new(0, nc+256);
2024 psz = RSTRING_PTR(obj);
2025 sprintf(psz, "#<BigDecimal:%"PRIxVALUE",'", self);
2026 tmp = psz + strlen(psz);
2027 VpToString(vp, tmp, 10, 0);
2028 tmp += strlen(tmp);
2029 sprintf(tmp, "',%"PRIuSIZE"(%"PRIuSIZE")>", VpPrec(vp)*VpBaseFig(), VpMaxPrec(vp)*VpBaseFig());
2030 rb_str_resize(obj, strlen(psz));
2031 return obj;
2032 }
2033
所以,您想要的似乎是检查字符串的第二部分,在您的情况下 0.1E2
等于 10
。上面的注释已经很清楚了,这应该是对象的完整数值。简单的正则表达式就足够了。
另一个选项:
"#<BigDecimal:95915c4,'0.1E2',9(27)>".split(",")[1].tr! "'", ''
=> "0.1E2"
BigDecimal#inspect 的文档不完整。考虑以下因素:
require 'bigdecimal`
BigDecimal.new("1.2345").inspect
#=> "#<BigDecimal:7fb06a110298,'0.12345E1',18(18)>"
...
BigDecimal.new("1.234567890").inspect
#=> "#<BigDecimal:7fb06a16ab58,'0.123456789E1',18(27)>"
BigDecimal.new("1.2345678901").inspect
#=> "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>"
BigDecimal.new("1.23456789012").inspect
#=> "#<BigDecimal:7fb06a1393a0,'0.1234567890 12E1',27(27)>"
BigDecimal.new("1.234567890123").inspect
#=> "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
从inspect
的源码可以看出,如果有效数字超过10位,则每10个字符之间用一个space隔开(为了便于阅读,大概):
BigDecimal.new("123.456789012345678901234567").inspect
#=> "#<BigDecimal:7fb06a0ac8b0,'0.1234567890 1234567890 1234567E3',36(36)>"
我建议按如下方式检索 BigDecimal
值的字符串表示形式:
str = "#<BigDecimal:7fb06a14a6a0,'0.1234567890 1E1',27(27)>"
str.delete(' ').split(?')[1]
#=> "0.12345678901E1"
我们还没有完成。我们仍然必须将提取的字符串转换为数字对象。我们不能使用BigDecimal#to_f,但是,如果该值的绝对值很大:
"1.23456789012345678".to_f
#=> 1.2345678901234567
最安全的做法是 return 一个 BigDecimal
对象,使用方法 BigDecimal::new,它有两个参数:
要转换为
BigDecimal
对象的值,可以是 Integer、Float、Rational、BigDecimal 或 String。如果是我们将提供的字符串,"spaces are ignored and unrecognized characters terminate the value"(类似于"123.4cat".to_f #=> 123.4
)。有效位数。如果省略或为零,则有效位数由值确定。我将省略这个论点。 (例如,
BigDecimal.new("0.1234E2").precs #=> [18, 18]
,其中数组包含当前和最大有效数字位数。
请注意,如果第一个参数是 Float 或 Rational,则第二个参数是必需的,否则它是可选的。
因此我们可以写成:
require 'bigdecimal'
def convert(str)
BigDecimal.new(str.delete(' ').split(?')[1])
end
convert "#<BigDecimal:7facd39d7ee8,'0.1234E4',9(18)>"
#=> #<BigDecimal:7facd39c7de0,'0.1234E4',9(18)>
convert "#<BigDecimal:7facd39b7be8,'0.1234E2',18(18)>"
#=> #<BigDecimal:7facd39ae610,'0.1234E2',18(18)>
convert "#<BigDecimal:7facd3990638,'0.1234E0',9(18)>"
#=> #<BigDecimal:7facd3980aa8,'0.1234E0',9(18)>
convert "#<BigDecimal:7facd3970e28,'0.1234E-2',9(18)>"
#=> #<BigDecimal:7facd39625d0,'0.1234E-2',9(18)>
v = convert "#<BigDecimal:7fb06a123780,'0.1234567890 123E1',27(27)>"
#=> #<BigDecimal:7fb069851d78,'0.1234567890 123E1',27(27)>
查看 BigDecimal
对象是否可以在不损失准确性的情况下转换为浮点数的简单方法是:
def convert_bd_to_float(bd)
f = bd.to_f
(bd==BigDecimal.new(f.to_s)) ? f : nil
end
convert_bd_to_float BigDecimal.new('1234567890123456')
#=> 1.234567890123456e+15
convert_bd_to_float BigDecimal.new('12345678901234567')
#=> nil