Swift:从字节数组创建任意基础字符串
Swift: Create arbitrary base string from byte array
我有一个 UInt8
的数组,我需要将其转换为以 36 为基数的字符串。我相信我需要将其转换为符合二进制整数的格式,但不确定如何转换。
let bArray: [UInt8] = ..... //some array of UInt8
let foo = ? // bArray -> a binary integer ... or another step?
let baseString = String(foo, radix: 36, uppercase: false)
这是正确的流程还是我应该使用不同的方法?
示例数据
使用 SHA1 对字符串 "test"
进行哈希处理以获取字节数组:
[169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47, 187, 211]
预期的 base36:
jrwjerxiekdtj9k82lg930wpkr6tq6r
和十六进制:
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
我已经 ,所以这里是 2...36
:
任何基础的通用版本
func bytesToRadix<C: RangeReplaceableCollection>(_ bytes: C, radix: Int, isUppercase: Bool = false, isBigEndian: Bool = true) -> String where C.Element == UInt8 {
// Nothing to process or radix outside of 2...36, return an empty string.
guard !bytes.isEmpty, 2...36 ~= radix else { return "" }
let bytes = isBigEndian ? bytes : C(bytes.reversed())
// For efficiency in calculation, combine 7 bytes into one Int.
let chunk = 7
let numvalues = bytes.count
var ints = Array(repeating: 0, count: (numvalues + chunk - 1)/chunk)
var rem = numvalues % chunk == 0 ? chunk : numvalues % chunk
var index = 0
var accum = 0
for value in bytes {
accum = (accum << 8) + Int(value)
rem -= 1
if rem == 0 {
rem = chunk
ints[index] = accum
index += 1
accum = 0
}
}
// Array to hold the result, in reverse order
var digits = [Int]()
// Repeatedly divide value by radix, accumulating the remainders.
// Repeat until original number is zero
while !ints.isEmpty {
var carry = 0
for (index, value) in ints.enumerated() {
var total = (carry << (8 * chunk)) + value
carry = total % radix
total /= radix
ints[index] = total
}
digits.append(carry)
// Remove leading Ints that have become zero.
ints = .init(ints.drop { [=10=] == 0 })
}
// Create mapping of digit Int to String
let letterOffset = Int(UnicodeScalar(isUppercase ? "A" : "a").value - 10)
let letters = (0 ..< radix).map { d in d < 10 ? "\(d)" : String(UnicodeScalar(letterOffset + d)!) }
// Reverse the digits array, convert them to String, and join them
return digits.reversed().map { letters[[=10=]] }.joined()
}
示例:
let face: [UInt8] = [0xFA, 0xCE]
print(bytesToRadix(face, radix: 16)) // "face"
print(bytesToRadix(face, radix: 16, isUppercase: true)) // "FACE"
print(bytesToRadix(face, radix: 16, isBigEndian: false)) // "cefa""
print(bytesToRadix(face, radix: 16, isUppercase: true, isBigEndian: false)) // "CEFA"
print(bytesToRadix(face, radix: 10)) // "64206"
print(bytesToRadix(face, radix: 2)) // "111101011001110"
print(bytesToRadix(face, radix: 36)) // "1dji"
// also works with Data
let faceData = Data([0xFA, 0xCE])
print(bytesToRadix(face, radix: 16)) // "face"
一些极端情况:
print(bytesToRadix([9], radix: 16)) // "9"
print(bytesToRadix([10], radix: 16)) // "a"
print(bytesToRadix([15], radix: 16)) // "f"
print(bytesToRadix([16], radix: 16)) // "10"
print(bytesToRadix([35], radix: 36)) // "z"
print(bytesToRadix([36], radix: 36)) // "10"
大考:
let bArray = (0...255).map(UInt8.init)
print(bytesToRadix(bArray, radix: 16, isBigEndian: false))
fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
print(bytesToRadix(bArray, radix: 36))
168swoi6iuzj4fbwknlnh695zl88v65qcfgnwrwepqcxb9dysmluowqahvt3r9gsc1v47ssxdivjda3nttl6r044pzz7zwhtgu2mkow5ts28x2mbwenh3wfz4s1sarspfhlrakvqrgpmzb66sgtz2lzbotl7r28wcq8925c747b44l60vrk3scrin4zvnwn7pdsukgo6lgjhu1nuwj7yt1h9ujpe3os17onsk7sp4ysmytu568do2tqetwnrmbxb2dtd8kqorcoakaizlm9svr8axe1acxfursz11nubrhighfd64yhmp99ucvzr944n8co01o4x64cmbd8be0hqbm2zy5uwe4uplc4sa50xajel4bkkxb1kh21pisna37eqwpbpq11ypr
使用您的示例数据进行测试:
let bArray: [UInt8] = [169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47, 187, 211]
print(bytesToRadix(bArray, radix: 16))
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
print(bytesToRadix(bArray, radix: 36))
jrwjerxiekdtj9k82lg930wpkr6tq6r
反转函数:radixToBytes
这里是反向函数的快速版本。它还不能使用大写数字或处理字节序(假定为大字节序)。
func radixToBytes(_ radixString: String, radix: Int) -> [UInt8] {
let digitMap: [Character : Int] = [
"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5,
"6": 6, "7": 7, "8": 8, "9": 9, "a": 10, "b": 11,
"c": 12, "d": 13, "e": 14, "f": 15, "g": 16, "h": 17,
"i": 18, "j": 19, "k": 20, "l": 21, "m": 22, "n": 23,
"o": 24, "p": 25, "q": 26, "r": 27, "s": 28, "t": 29,
"u": 30, "v": 31, "w": 32, "x": 33, "y": 34, "z": 35
]
// Convert input string into array of Int digits
let digits = Array(radixString).compactMap { digitMap[[=17=]] }
// Nothing to process? Return an empty array.
guard digits.count > 0 else { return [] }
let numdigits = digits.count
// Array to hold the result, in reverse order
var bytes = [UInt8]()
// Convert array of digits into array of Int values each
// representing 6 digits of the original number. Six digits
// was chosen to work on 32-bit and 64-bit systems.
// Compute length of first number. It will be less than 6 if
// there isn't a multiple of 6 digits in the number.
let chunk = 6
var ints = Array(repeating: 0, count: (numdigits + chunk - 1)/chunk)
var rem = numdigits % chunk
if rem == 0 {
rem = chunk
}
var index = 0
var accum = 0
for digit in digits {
accum = accum * radix + digit
rem -= 1
if rem == 0 {
rem = chunk
ints[index] = accum
index += 1
accum = 0
}
}
// Repeatedly divide value by 256, accumulating the remainders.
// Repeat until original number is zero
var mult = 1
for _ in 1...chunk {
mult *= radix
}
while ints.count > 0 {
var carry = 0
for (index, value) in ints.enumerated() {
var total = carry * mult + value
carry = total % 256
total /= 256
ints[index] = total
}
bytes.append(UInt8(truncatingIfNeeded: carry))
// Remove leading Ints that have become zero
ints = .init(ints.drop { [=17=] == 0 })
}
// Reverse the array and return it
return bytes.reversed()
}
看起来您正在尝试将数据(字节)转换为整数,然后再转换为基数 36:
let bArray: [UInt8] = [255, 255, 255, 255, 255, 255, 255, 127] // Int.max 9223372036854775807
let foo = bArray.withUnsafeBytes { [=10=].load(as: Int.self) }
let baseString = String(foo, radix: 36, uppercase: true) // "1Y2P0IJ32E8E7"
String(Int.max, radix: 36, uppercase: true) // 1Y2P0IJ32E8E7
我有一个 UInt8
的数组,我需要将其转换为以 36 为基数的字符串。我相信我需要将其转换为符合二进制整数的格式,但不确定如何转换。
let bArray: [UInt8] = ..... //some array of UInt8
let foo = ? // bArray -> a binary integer ... or another step?
let baseString = String(foo, radix: 36, uppercase: false)
这是正确的流程还是我应该使用不同的方法?
示例数据
使用 SHA1 对字符串 "test"
进行哈希处理以获取字节数组:
[169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47, 187, 211]
预期的 base36:
jrwjerxiekdtj9k82lg930wpkr6tq6r
和十六进制:
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
我已经 2...36
:
func bytesToRadix<C: RangeReplaceableCollection>(_ bytes: C, radix: Int, isUppercase: Bool = false, isBigEndian: Bool = true) -> String where C.Element == UInt8 {
// Nothing to process or radix outside of 2...36, return an empty string.
guard !bytes.isEmpty, 2...36 ~= radix else { return "" }
let bytes = isBigEndian ? bytes : C(bytes.reversed())
// For efficiency in calculation, combine 7 bytes into one Int.
let chunk = 7
let numvalues = bytes.count
var ints = Array(repeating: 0, count: (numvalues + chunk - 1)/chunk)
var rem = numvalues % chunk == 0 ? chunk : numvalues % chunk
var index = 0
var accum = 0
for value in bytes {
accum = (accum << 8) + Int(value)
rem -= 1
if rem == 0 {
rem = chunk
ints[index] = accum
index += 1
accum = 0
}
}
// Array to hold the result, in reverse order
var digits = [Int]()
// Repeatedly divide value by radix, accumulating the remainders.
// Repeat until original number is zero
while !ints.isEmpty {
var carry = 0
for (index, value) in ints.enumerated() {
var total = (carry << (8 * chunk)) + value
carry = total % radix
total /= radix
ints[index] = total
}
digits.append(carry)
// Remove leading Ints that have become zero.
ints = .init(ints.drop { [=10=] == 0 })
}
// Create mapping of digit Int to String
let letterOffset = Int(UnicodeScalar(isUppercase ? "A" : "a").value - 10)
let letters = (0 ..< radix).map { d in d < 10 ? "\(d)" : String(UnicodeScalar(letterOffset + d)!) }
// Reverse the digits array, convert them to String, and join them
return digits.reversed().map { letters[[=10=]] }.joined()
}
示例:
let face: [UInt8] = [0xFA, 0xCE]
print(bytesToRadix(face, radix: 16)) // "face"
print(bytesToRadix(face, radix: 16, isUppercase: true)) // "FACE"
print(bytesToRadix(face, radix: 16, isBigEndian: false)) // "cefa""
print(bytesToRadix(face, radix: 16, isUppercase: true, isBigEndian: false)) // "CEFA"
print(bytesToRadix(face, radix: 10)) // "64206"
print(bytesToRadix(face, radix: 2)) // "111101011001110"
print(bytesToRadix(face, radix: 36)) // "1dji"
// also works with Data
let faceData = Data([0xFA, 0xCE])
print(bytesToRadix(face, radix: 16)) // "face"
一些极端情况:
print(bytesToRadix([9], radix: 16)) // "9"
print(bytesToRadix([10], radix: 16)) // "a"
print(bytesToRadix([15], radix: 16)) // "f"
print(bytesToRadix([16], radix: 16)) // "10"
print(bytesToRadix([35], radix: 36)) // "z"
print(bytesToRadix([36], radix: 36)) // "10"
大考:
let bArray = (0...255).map(UInt8.init)
print(bytesToRadix(bArray, radix: 16, isBigEndian: false))
fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a09f9e9d9c9b9a999897969594939291908f8e8d8c8b8a898887868584838281807f7e7d7c7b7a797877767574737271706f6e6d6c6b6a696867666564636261605f5e5d5c5b5a595857565554535251504f4e4d4c4b4a494847464544434241403f3e3d3c3b3a393837363534333231302f2e2d2c2b2a292827262524232221201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100
print(bytesToRadix(bArray, radix: 36))
168swoi6iuzj4fbwknlnh695zl88v65qcfgnwrwepqcxb9dysmluowqahvt3r9gsc1v47ssxdivjda3nttl6r044pzz7zwhtgu2mkow5ts28x2mbwenh3wfz4s1sarspfhlrakvqrgpmzb66sgtz2lzbotl7r28wcq8925c747b44l60vrk3scrin4zvnwn7pdsukgo6lgjhu1nuwj7yt1h9ujpe3os17onsk7sp4ysmytu568do2tqetwnrmbxb2dtd8kqorcoakaizlm9svr8axe1acxfursz11nubrhighfd64yhmp99ucvzr944n8co01o4x64cmbd8be0hqbm2zy5uwe4uplc4sa50xajel4bkkxb1kh21pisna37eqwpbpq11ypr
使用您的示例数据进行测试:
let bArray: [UInt8] = [169, 74, 143, 229, 204, 177, 155, 166, 28, 76, 8, 115, 211, 145, 233, 135, 152, 47, 187, 211]
print(bytesToRadix(bArray, radix: 16))
a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
print(bytesToRadix(bArray, radix: 36))
jrwjerxiekdtj9k82lg930wpkr6tq6r
反转函数:radixToBytes
这里是反向函数的快速版本。它还不能使用大写数字或处理字节序(假定为大字节序)。
func radixToBytes(_ radixString: String, radix: Int) -> [UInt8] {
let digitMap: [Character : Int] = [
"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5,
"6": 6, "7": 7, "8": 8, "9": 9, "a": 10, "b": 11,
"c": 12, "d": 13, "e": 14, "f": 15, "g": 16, "h": 17,
"i": 18, "j": 19, "k": 20, "l": 21, "m": 22, "n": 23,
"o": 24, "p": 25, "q": 26, "r": 27, "s": 28, "t": 29,
"u": 30, "v": 31, "w": 32, "x": 33, "y": 34, "z": 35
]
// Convert input string into array of Int digits
let digits = Array(radixString).compactMap { digitMap[[=17=]] }
// Nothing to process? Return an empty array.
guard digits.count > 0 else { return [] }
let numdigits = digits.count
// Array to hold the result, in reverse order
var bytes = [UInt8]()
// Convert array of digits into array of Int values each
// representing 6 digits of the original number. Six digits
// was chosen to work on 32-bit and 64-bit systems.
// Compute length of first number. It will be less than 6 if
// there isn't a multiple of 6 digits in the number.
let chunk = 6
var ints = Array(repeating: 0, count: (numdigits + chunk - 1)/chunk)
var rem = numdigits % chunk
if rem == 0 {
rem = chunk
}
var index = 0
var accum = 0
for digit in digits {
accum = accum * radix + digit
rem -= 1
if rem == 0 {
rem = chunk
ints[index] = accum
index += 1
accum = 0
}
}
// Repeatedly divide value by 256, accumulating the remainders.
// Repeat until original number is zero
var mult = 1
for _ in 1...chunk {
mult *= radix
}
while ints.count > 0 {
var carry = 0
for (index, value) in ints.enumerated() {
var total = carry * mult + value
carry = total % 256
total /= 256
ints[index] = total
}
bytes.append(UInt8(truncatingIfNeeded: carry))
// Remove leading Ints that have become zero
ints = .init(ints.drop { [=17=] == 0 })
}
// Reverse the array and return it
return bytes.reversed()
}
看起来您正在尝试将数据(字节)转换为整数,然后再转换为基数 36:
let bArray: [UInt8] = [255, 255, 255, 255, 255, 255, 255, 127] // Int.max 9223372036854775807
let foo = bArray.withUnsafeBytes { [=10=].load(as: Int.self) }
let baseString = String(foo, radix: 36, uppercase: true) // "1Y2P0IJ32E8E7"
String(Int.max, radix: 36, uppercase: true) // 1Y2P0IJ32E8E7