spark:将十六进制字符串转换为十进制

6l7fqoea  于 2021-05-26  发布在  Spark
关注(0)|答案(1)|浏览(1224)

在spark中有没有办法将md5散列转换成数字列?
尝试直接转换为十进制。也尝试过使用conv.but neigher work(请参见下文)。奇怪的是conv将不同的哈希值转换为相同的值。

(1 to 5).toDF("id")
.withColumn("md5_id", md5($"id".cast("string")))
.withColumn("conv_id", conv($"md5_id", 16, 10))
.withColumn("num_id", $"md5_id".cast(DecimalType(38,0)))
.show(false)

+---+--------------------------------+--------------------+-------+
|id |md5_id                          |conv_id             |num_id|
+---+--------------------------------+--------------------+-------+
|1  |c4ca4238a0b923820dcc509a6f75849b|18446744073709551615|null   |
|2  |c81e728d9d4c2f636f067f89cc14862c|18446744073709551615|null   |
|3  |eccbc87e4b5ce2fe28308fd9f2a7baf3|18446744073709551615|null   |
|4  |a87ff679a2f3e71d9181a67b7542122c|18446744073709551615|null   |
|5  |e4da3b7fbbce2345d7772b0674a318d5|18446744073709551615|null   |
+---+--------------------------------+--------------------+-------+

更新
如果其他人遇到同样的问题,这就满足了我的需要:

def toNum = udf((hex: String) =>
      new java.math.BigInteger(hex.toUpperCase, 16)
    )

    (1 to 5).toDF("id")
      .withColumn("hashed_id", toNum(substring(md5($"id".cast("string")), 0, 31)))
      .show(false)

+---+--------------------------------------+
|id |hashed_id                             |
+---+--------------------------------------+
|1  |16348679641551244288068877217848318025|
|2  |16625230717330387431313232838613092450|
|3  |19672244359719724500030062827806555055|
|4  |13998420256418836516930895031794147618|
|5  |19012319408524223020738990858985681293|
+---+--------------------------------------+

root
 |-- id: integer (nullable = false)
 |-- hashed_id: decimal(38,0) (nullable = true)
n9vozmp4

n9vozmp41#

嗯,这个案子很复杂,我会告诉你原因的。将md5转换为数字通常会创建一个 BigInteger . 让我来演示一下如何在没有Spark的情况下完成:

scala> val hex = "eccbc87e4b5ce2fe28308fd9f2a7baf3"
scala> new java.math.BigInteger(hex.toUpperCase, 16)
res28: java.math.BigInteger = 314755909755515592000481005244904880883

正如你所看到的,这个数字是巨大的,实际上大于你想要转换的38的十进制数,这个数字是39位。而spark不支持这种默认的数据类型转换。因此,解决这个问题的一种方法是使用带有自定义项的十进制库,但是会降低数据的精度。下面是如何做到这一点:

def md5toIntString = udf((hex: String) =>
    Decimal(new java.math.BigInteger(hex.toUpperCase, 16)).toString
)
(1 to 5).toDF("id")
.withColumn("md5_id", md5($"id".cast("string")))
.withColumn("conv_id", conv($"md5_id", 16, 10))
.withColumn("num_id", md5toIntString($"md5_id"))
.show(false)

你可以看到结果是:

+---+--------------------------------+--------------------+---------------------------------------+
|id |md5_id                          |conv_id             |num_id                                 |
+---+--------------------------------+--------------------+---------------------------------------+
|1  |c4ca4238a0b923820dcc509a6f75849b|18446744073709551615|261578874264819908609102035485573088411|
|2  |c81e728d9d4c2f636f067f89cc14862c|18446744073709551615|266003691477286198901011725417809479212|
|3  |eccbc87e4b5ce2fe28308fd9f2a7baf3|18446744073709551615|314755909755515592000481005244904880883|
|4  |a87ff679a2f3e71d9181a67b7542122c|18446744073709551615|223974724102701384270894320508706361900|
|5  |e4da3b7fbbce2345d7772b0674a318d5|18446744073709551615|304197110536387568331823853743770900693|
+---+--------------------------------+--------------------+---------------------------------------+

所以这就是为什么您要面对这个问题,md5数据的大小介于 1 and len(2^128-1) 大约是39位。
因此,我建议或使用字符串,或转换成另一种类型。

相关问题