问题
To keep this simple, I'll only be encoding/decoding a single byte.
If I encode the byte 127, I get the base64 string "fw==" which can be successfully decoded back to byte 127. If, however, I encode a byte ≥ 128, then even though I can produce a base64 string without error (for example, byte 128 gives the string "gA=="), I get an error when I try to decode it.
Here's my code which can be copy-pasted into any Xcode playground to reproduce the problem:
func stringToByteArray(string: String) -> [UInt8] {
var bytes: [UInt8] = [];
for code in string.utf8 {
bytes.append(UInt8(code));
}
return bytes;
}
func byteArrayToBase64(bytes: [UInt8]) -> String {
let nsdata: NSData = NSData(bytes: bytes as [Byte], length: bytes.count)
let base64Encoded: NSString = nsdata.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0));
return String(base64Encoded);
}
func base64ToByteArray(base64String: String) -> [UInt8] {
let nsdata: NSData = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions(rawValue: 0))!
let base64Decoded: NSString = NSString(data: nsdata, encoding: NSUTF8StringEncoding)!
return stringToByteArray(String(base64Decoded));
}
/* Replacing 127 with 128 below or greater produces an error */
var testString = byteArrayToBase64([127]);
base64ToByteArray(testString)
回答1:
The problem is here:
let base64Decoded: NSString = NSString(data: nsdata, encoding: NSUTF8StringEncoding)!
You convert the decoded data to a string. This fails for
[128]
because that does not represent a valid UTF-8 sequence.
Here is a version that avoids the intermediate string:
func base64ToByteArray(base64String: String) -> [UInt8] {
let nsdata: NSData = NSData(base64EncodedString: base64String, options: NSDataBase64DecodingOptions(rawValue: 0))!
// Create array of the required size ...
var bytes = [UInt8](count: nsdata.length, repeatedValue: 0)
// ... and fill it with the data
nsdata.getBytes(&bytes)
return bytes
}
Remarks:
options: NSDataBase64DecodingOptions(rawValue: 0)
can be simplified tooptions: nil
.- There are some unnecessary type annotations and conversions in your code.
- Your function crashes if
baseString
is not a valid Base64 string. You could change it to return an optional.
Then it would look like this:
func byteArrayToBase64(bytes: [UInt8]) -> String {
let nsdata = NSData(bytes: bytes, length: bytes.count)
let base64Encoded = nsdata.base64EncodedStringWithOptions(nil);
return base64Encoded;
}
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
}
Example usage:
let testString = byteArrayToBase64([127, 128, 0, 130]);
println(testString) // Output: f4AAgg==
if let result = base64ToByteArray(testString) {
println(result) // Output: [127, 128, 0, 130]
} else {
println("failed")
}
Update for Swift 2 / Xcode 7:
func byteArrayToBase64(bytes: [UInt8]) -> String {
let nsdata = NSData(bytes: bytes, length: bytes.count)
let base64Encoded = nsdata.base64EncodedStringWithOptions([]);
return base64Encoded;
}
func base64ToByteArray(base64String: String) -> [UInt8]? {
if let nsdata = NSData(base64EncodedString: base64String, options: []) {
var bytes = [UInt8](count: nsdata.length, repeatedValue: 0)
nsdata.getBytes(&bytes, length: bytes.count)
return bytes
}
return nil // Invalid input
}
let testString = byteArrayToBase64([127, 128, 0, 130]);
print(testString) // Output: f4AAgg==
if let result = base64ToByteArray(testString) {
print(result) // Output: [127, 128, 0, 130]
} else {
print("failed")
}
来源:https://stackoverflow.com/questions/26826898/error-when-decoding-certain-base64-strings-but-not-others