我在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
}
但它会返回不同的结果。我很少与返回C#方法的东西混淆。这些方法的输入是这个字符串:
kc_jYgaSXyZ0c7oAAACQAQAAAO560uWYfkmUGkgU7Gbn7Cs =
C#方法返回包含35个项目的字节数组。我在快速方法中获得了48个具有不同值的项目。我知道它不会像Base64那样长,但我必须使用它。
为什么.NET方法返回的项目较少?如何实现我的swift方法以获得与C#相同的结果?
答案 0 :(得分:5)
正如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#相同。
在Swift 2中它应该是:
if let nsdata = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) {
...
答案 1 :(得分:0)
要将base64字符串转换为原始数据,仅将字符转换为其UTF等效项就不足以从place-value数组中获取字符索引(通常是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
}
请注意,在我们完成聚合和重新分割位之后,可能会有一个不能被3整除的余数。这是=
的用武之地。这些被称为&#39;填充&#39;并在编码字符串中表示占位符字节。我们只需抓住剩下的东西,然后将这些字节添加到缓冲区的末尾。
对于=
,我们抓住最左边的两个字节
byte24 & (mask8 << 16)) >> 16
byte24 & (mask8 << 8)) >> 8
对于==
,我们只抓取最左边的字节:
byte24 & (mask8 << 16)) >> 16
当然,正如Martin和Libor已经提到的,在实践中最直接的方法是使用NSData转换你的字符串:
NSData(base64EncodedString: base64String, options: nil)
然后使用NSData
&#39; getBytes
方法提取字节。
答案 2 :(得分:0)
这里只是在swift 3中工作的版本,并且代码不会被弃用
if let nsdata1 = Data(base64Encoded: stringData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) {
let arr2 = nsdata1.withUnsafeBytes {
Array(UnsafeBufferPointer<UInt8>(start: $0, count: nsdata1.count/MemoryLayout<UInt8>.size))
}
print("Array: ",arr2)
}