将 base64 字符串转换为字节数组,如 C# 方法 Convert.FromBase64String
convert base64 string to byte array like C# method Convert.FromBase64String
我在 C# 中有这个示例方法:
public static byte[] FromBase64Url(string data)
{
data = data.Replace('-', '+').Replace('_', '/');
return Convert.FromBase64String(data);
}
我想在 Swift 中实现它。我试过这个:
class func fromBase64(baseString: String) -> [UInt8] {
let data = baseString.replace("-", withString: "+").replace("_", withString: "/")
var buff = [UInt8]()
buff += data.utf8
return buff
}
但它 returns 不同的结果。我对什么 returns C# 方法有点困惑。这些方法的输入是这个字符串:
kc_jYgaSXyZ0c7oAAACQAQAAAO560uWYfkmUGkgU7Gbn7Cs=
C# 方法 returns 包含 35 个项目的字节数组。我在 swift 方法中得到了 48 个具有不同值的项目。我知道它不应该像 Base64 那样长,但我必须使用它。
为什么 .NET 方法 returns 项少?如何实现我的 swift 方法以获得与 C# 相同的结果?
正如 Martin R 在他的评论中提到的那样,我尝试了其他主题的选项,但我正在以错误的方式转换字节数组。这是 swift:
中的正确方法
class func base64ToByteArray(base64String: String) -> [UInt8]? {
if let nsdata = NSData(base64EncodedString: base64String, options: nil) {
var bytes = [UInt8](count: nsdata.length, repeatedValue: 0)
nsdata.getBytes(&bytes)
return bytes
}
return nil // Invalid input
}
现在我得到了与在 C# 中相同的结果。
在Swift2中应该是:
if let nsdata = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) {
...
要将 base64 字符串转换为原始数据,仅将字符转换为它们的 UTF 等效项是不够的,您可以从位值数组(通常是 MIME 的 base64 实现)中获取字符索引:
let MIME64: [UnicodeScalar] = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"+", "/"
]
现在 base64 字符串中的每个字符代表 6 位数据。要转换为字节,您可以将字符分组为 4 个字符,每个字符提取 3 个字节的数据。例如,您的示例字符串 kc/j
转换为 MIME64 数组中的索引 [36, 28, 63, 35]
。我们采用这些索引并通过将位移动到适当的位置来构建一个 24 位 Int:var int24 = (36 << 18)|(28 << 12)|(63 << 6)|35
或 100100011100111111100011
。该二进制文件可以分为 3 个不同的字节:10010001
、11001111
、11100011
,然后我们将其附加到我们的缓冲区。这就是为什么您的原始方法返回 48 字节而不是 35 字节数组的原因。这是实际的公式:
func fromBase64(baseString: String) -> [UInt8] {
var buff = [UInt8]()
let mask8 = 0b11111111
var i = 3
var byte24 = 0
for a in baseString.unicodeScalars {
if let aInt = find(MIME64, a) {
var shiftAmount = 6 * (i % 4)
println(a, aInt)
byte24 |= aInt << shiftAmount
if i > 0 {
--i
} else {
i = 3
var bytes = [
(byte24 & (mask8 << 16)) >> 16,
(byte24 & (mask8 << 8)) >> 8,
byte24 & mask8
]
buff.append(UInt8(bytes[0]))
buff.append(UInt8(bytes[1]))
buff.append(UInt8(bytes[2]))
byte24 = 0
}
}
}
switch i {
case 0:
var bytes = [
(byte24 & (mask8 << 16)) >> 16,
(byte24 & (mask8 << 8)) >> 8,
]
buff.append(UInt8(bytes[0]))
buff.append(UInt8(bytes[1]))
case 1:
var bytes = [
(byte24 & (mask8 << 16)) >> 16,
]
buff.append(UInt8(bytes[0]))
default:
break;
}
return buff
}
请注意,在我们完成位的聚合和重新拆分之后,可能会有一个不能被三整除的余数。这就是 =
的用武之地。这些被称为 'padding' 并代表编码字符串中的占位符字节。我们只需抓住剩下的任何内容并将这些字节添加到缓冲区的末尾。
对于=
,我们抓取最左边的两个字节
byte24 & (mask8 << 16)) >> 16
byte24 & (mask8 << 8)) >> 8
对于==
,我们只抓取最左边的字节:
byte24 & (mask8 << 16)) >> 16
当然,正如 Martin 和 Libor 已经提到的,在实践中最直接的方法是只使用 NSData 来转换您的字符串:
NSData(base64EncodedString: base64String, options: nil)
然后使用NSData
的getBytes
方法提取字节。
这里是在 swift 3 中工作的简单版本,并且代码没有被弃用
if let nsdata1 = Data(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) {
let arr2 = nsdata1.withUnsafeBytes {
Array(UnsafeBufferPointer<UInt8>(start: [=10=], count: nsdata1.count/MemoryLayout<UInt8>.size))
}
print("Array: ",arr2)
}
我在 C# 中有这个示例方法:
public static byte[] FromBase64Url(string data)
{
data = data.Replace('-', '+').Replace('_', '/');
return Convert.FromBase64String(data);
}
我想在 Swift 中实现它。我试过这个:
class func fromBase64(baseString: String) -> [UInt8] {
let data = baseString.replace("-", withString: "+").replace("_", withString: "/")
var buff = [UInt8]()
buff += data.utf8
return buff
}
但它 returns 不同的结果。我对什么 returns C# 方法有点困惑。这些方法的输入是这个字符串:
kc_jYgaSXyZ0c7oAAACQAQAAAO560uWYfkmUGkgU7Gbn7Cs=
C# 方法 returns 包含 35 个项目的字节数组。我在 swift 方法中得到了 48 个具有不同值的项目。我知道它不应该像 Base64 那样长,但我必须使用它。
为什么 .NET 方法 returns 项少?如何实现我的 swift 方法以获得与 C# 相同的结果?
正如 Martin R 在他的评论中提到的那样,我尝试了其他主题的选项,但我正在以错误的方式转换字节数组。这是 swift:
中的正确方法class func base64ToByteArray(base64String: String) -> [UInt8]? {
if let nsdata = NSData(base64EncodedString: base64String, options: nil) {
var bytes = [UInt8](count: nsdata.length, repeatedValue: 0)
nsdata.getBytes(&bytes)
return bytes
}
return nil // Invalid input
}
现在我得到了与在 C# 中相同的结果。
在Swift2中应该是:
if let nsdata = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) {
...
要将 base64 字符串转换为原始数据,仅将字符转换为它们的 UTF 等效项是不够的,您可以从位值数组(通常是 MIME 的 base64 实现)中获取字符索引:
let MIME64: [UnicodeScalar] = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"+", "/"
]
现在 base64 字符串中的每个字符代表 6 位数据。要转换为字节,您可以将字符分组为 4 个字符,每个字符提取 3 个字节的数据。例如,您的示例字符串 kc/j
转换为 MIME64 数组中的索引 [36, 28, 63, 35]
。我们采用这些索引并通过将位移动到适当的位置来构建一个 24 位 Int:var int24 = (36 << 18)|(28 << 12)|(63 << 6)|35
或 100100011100111111100011
。该二进制文件可以分为 3 个不同的字节:10010001
、11001111
、11100011
,然后我们将其附加到我们的缓冲区。这就是为什么您的原始方法返回 48 字节而不是 35 字节数组的原因。这是实际的公式:
func fromBase64(baseString: String) -> [UInt8] {
var buff = [UInt8]()
let mask8 = 0b11111111
var i = 3
var byte24 = 0
for a in baseString.unicodeScalars {
if let aInt = find(MIME64, a) {
var shiftAmount = 6 * (i % 4)
println(a, aInt)
byte24 |= aInt << shiftAmount
if i > 0 {
--i
} else {
i = 3
var bytes = [
(byte24 & (mask8 << 16)) >> 16,
(byte24 & (mask8 << 8)) >> 8,
byte24 & mask8
]
buff.append(UInt8(bytes[0]))
buff.append(UInt8(bytes[1]))
buff.append(UInt8(bytes[2]))
byte24 = 0
}
}
}
switch i {
case 0:
var bytes = [
(byte24 & (mask8 << 16)) >> 16,
(byte24 & (mask8 << 8)) >> 8,
]
buff.append(UInt8(bytes[0]))
buff.append(UInt8(bytes[1]))
case 1:
var bytes = [
(byte24 & (mask8 << 16)) >> 16,
]
buff.append(UInt8(bytes[0]))
default:
break;
}
return buff
}
请注意,在我们完成位的聚合和重新拆分之后,可能会有一个不能被三整除的余数。这就是 =
的用武之地。这些被称为 'padding' 并代表编码字符串中的占位符字节。我们只需抓住剩下的任何内容并将这些字节添加到缓冲区的末尾。
对于=
,我们抓取最左边的两个字节
byte24 & (mask8 << 16)) >> 16
byte24 & (mask8 << 8)) >> 8
对于==
,我们只抓取最左边的字节:
byte24 & (mask8 << 16)) >> 16
当然,正如 Martin 和 Libor 已经提到的,在实践中最直接的方法是只使用 NSData 来转换您的字符串:
NSData(base64EncodedString: base64String, options: nil)
然后使用NSData
的getBytes
方法提取字节。
这里是在 swift 3 中工作的简单版本,并且代码没有被弃用
if let nsdata1 = Data(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) {
let arr2 = nsdata1.withUnsafeBytes {
Array(UnsafeBufferPointer<UInt8>(start: [=10=], count: nsdata1.count/MemoryLayout<UInt8>.size))
}
print("Array: ",arr2)
}