Puis-je construire un BigInt avec n'importe quel tableau d'octets (Scala)?
-
10-07-2019 - |
Question
J'essaie de représenter le résultat d'un hachage MD5 dans la chaîne la plus courte possible. Il semble inutile de le transformer en une chaîne hexagonale et de laisser G à Z se perdre.
Une des idées que j'ai eu est d'obtenir le hachage MD5 de mon entrée sous forme de tableau d'octets et de construire un BigInt
avec ce dernier. Je peux ensuite appeler toString (36)
et obtenir le numéro sous forme de base-36 dans une chaîne ( -? [0-9a-z] *
, le numéro peut être positif ou négatif). Cela fonctionne pour moi.
Le problème, c'est que je ne suis pas sûr qu'un BigInt
puisse être construit avec un tableau d'octets, et je ne peux pas le prouver avec des tests (du moins pas à temps!). Je suppose que oui, car je comprends qu'un BigInt peut être de taille arbitraire. Je ne peux pas utiliser cette méthode avant de savoir avec certitude que cela fonctionnera pour toutes les sorties possibles. Alors, quelqu'un peut-il me dire si cela fonctionnera pour toutes les entrées (ou comment convertir facilement un tableau d'octets afin qu'il puisse être représenté en base 36).
Clarification : j'ai l'implémentation, je vous pose des questions sur le comportement de l'ensemble du domaine (00000000000000000000000000000000 à FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
La solution
En vous basant sur vos commentaires ci-dessus, la mise en œuvre suivante codera / décodera de manière fiable un tableau d'octets arbitraire:
package blevins.example
object BigIntEncoder {
val radix = 36
implicit def byteArrayToString(ba: Array[Byte]): String = {
new java.math.BigInteger(addByte(ba)).toString(radix)
}
implicit def stringToByteArray(s: String): Array[Byte] = {
stripByte(new java.math.BigInteger(s, radix).toByteArray)
}
def addByte(ba: Array[Byte]): Array[Byte] = {
val h = new Array[Byte](1)
h(0) = 0x01
h ++ ba
}
def stripByte(ba: Array[Byte]): Array[Byte] = {
ba.slice(1,ba.size)
}
}
Notez que nous ajoutons un octet supplémentaire 0x01 à la tête du tableau pour éviter tout effet secondaire résultant de la prise du complément à deux du tableau d'octets.
EDIT: les tests nécessaires pour prouver cette erreur sont documentés ici: http: // cleverlytitled.blogspot.com/2009/10/scalacheck.html
Autres conseils
Le codage Base64 ne serait-il pas plus court que Base36? Vous pouvez trouver de nombreuses implémentations autour.
Mais pour répondre à la question:
// Make a big randomly-filled byte array
val random = scala.util.Random
val arraySize = 8543
val bytes: Array[Byte] = new Array[Byte](arraySize) // make some big array
random.nextBytes(bytes) // fill it randomly
// Make a BigInt out of it and the corresponding base36 string representation
val bi: BigInt = new BigInt(new java.math.BigInteger(bytes))
val strRep: String = bi.toString(36)
// Make a new BigInt out of the string rep. Does it match?
val bi2: BigInt = new BigInt(new java.math.BigInteger(strRep, 36))
if (bi == bi2) {
println("yippee!!")
}
// Make a new byte array out of the BigInt. Does it match the original array?
val bytes2: Array[Byte] = bi2.toByteArray
if (bytes deepEquals bytes2) {
println("yippee again!!")
}