将base64字符串转换为字节数组,如C#方法Convert.FromBase64String

时间:2015-03-06 15:51:08

标签: c# swift base64

我在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#相同的结果?

3 个答案:

答案 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)|35100100011100111111100011。该二进制文件可以分为3个不同的字节:100100011100111111100011,然后我们将其附加到缓冲区。这就是原始方法返回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)  
}