I need to encode a regular string (String or NSString) to a Code Page 850 format.
There's an external String enconding who supports this format (It's called dosLatin1 in the CFStringEncoding enum). I don't know if it's can really do the work, but it's the only reference that I found to Code Page 850 in the whole iOS documentation.
How can I use the CFStringEnconding to convert a "regular" string to a string at a CP850 format? Is it the best way to do it?
If you can get by with CP 1252 which is the "modern" replacement for 850, then you can use Swift String's built in conversion. Otherwise, you can try using Core Foundation's conversion method.
let swiftString = "This is a string"
// Easy way -- use Swift String plus "modern" CP 1252 eoncding to get a block of data. Note: does not include BOM
if let data = swiftString.data(using: .windowsCP1252, allowLossyConversion: true) {
print(data) // Do something with the resulting data
}
// The more thorough way to use CP 850 (enum value = 1040) -- use Core Foundation. This will produce a BOM if necessary.
let coreFoundationString = swiftString as CFString
let count = CFStringGetLength(coreFoundationString) * 2
var buffer = [UInt8](repeating: 0, count: count)
let resultCount = CFStringGetBytes(coreFoundationString as CFString, CFRangeMake(0, CFStringGetLength(coreFoundationString)), 1040, 0x41, true, &buffer, count, nil)
print(buffer)
Related
In my project, I'm dealing with a UInt8 buffer. Here's my code:
let bufferSize = 4096
var buffer = [UInt8](repeating: 0, count: bufferSize)
Now I need to create another buffer in UTF8 format to command and receive response for a new device, but I'm not sure how to deal with this error:
Cannot convert value of type 'Int' to expected argument type 'UTF8' (aka 'Unicode.UTF8')
let bufferSize = 4096
var buffer = [UTF8](repeating: 0, count: bufferSize)
UTF8 is an encoding, this means a way to convert bytes into actual characters (mainly with String class).
So when you are receiving "UTF8", it means you are actually receiving or sending bytes, which, when decoded through UTF8, are characters, or strings.
To manage bytes, you can either use [UInt8] arrays, as mentioned, or Data class, which is dedicated for this.
Then, you can use this to convert from [UInt8] to String :
let array : [UInt8] = [0x05, 0x26, 0xFD, 0xB5] // example
let data = Data(array)
let resultString = String(data: data, encoding: .utf8)
And to convert from String to [UInt8]:
let string = "To be converted to UTF8 bytes..."
let data = string.data(using: .utf8)!
let array = [UInt8](data)
So your first code extract is correct.
But your second is not correct. UTF8 is just an enum (?) representing the UTF8 encoding itself, not a character nor a byte in UTF8 encoding, so there is no interest in doing an [UTF8] array.
Your output can simply be a String. I do not think you have an expected argument to be of swift type UTF8.
I'm calling a function in an objective c class from swift.
-(char *)decrypt:(char *)crypt el:(int)el{}
when calling this function from swift, it asks for an UnsafeMutablePointer<Int8> as the value for the parameter 'crypt'
the value for the 'crypt' is comming from a server and it is a base64encoded string. So I decode that string and got a Data object.
let resultData = Data(base64Encoded: base64String)
Now I need to pass this data to the above mentioned function. I have tried to convert this Data object to a UnsafeMutablePointer<Int8>
resultData?.withUnsafeBytes { (u8Ptr: UnsafeMutablePointer<Int8>) in
let decBytes = tea?.decrypt(u8Ptr , el: el)}
But it is not compiling. Gives below error
'UnsafeMutablePointer' is not convertible to 'UnsafePointer<_>'
I don't know much about objective c. So could anyone help me to pass this parameter to objective c function.
you have to change UnsafeMutablePointer to UnsafePointer
UnsafePointer
resultData?.withUnsafeBytes {(bytes: UnsafePointer<CChar>)->Void in
//Use `bytes` inside this closure
}
UnsafeMutablePointer
var data2 = Data(capacity: 1024)
data2.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> Void in
//Use `bytes` inside this closure
})
Edit, updated my answer for two things:
Not returning the pointer from withUnsafeBytes
Accounting for Swift 5' deprecation warning: 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead
// buffer pointer captured is converted to byte pointer which is used in the block to decode the base64 encoded Data
encodedStringData.withUnsafeMutableBytes { (rawBufferPtr: UnsafeMutableRawBufferPointer) in
if let rawPtr = rawBufferPtr.baseAddress {
let decodedString = String(bytesNoCopy: rawPtr, length: rawBufferPtr.count, encoding: .utf8, freeWhenDone: false)
print(decodedString!)
}
}
someData.withUnsafeBytes { (bufferRawPtr: UnsafeRawBufferPointer) in
// For converting an UnsafeRawBufferPointer to its typed variant, in this case UnsafeBufferPointer<UInt8>
let bufferTypedPtr = bufferRawPtr.bindMemory(to: UInt8.self)
// Then, getting the typed UnsafePointer, in this case UnsafePointer<UInt8>
let unsafePointer = bufferTypedPtr.baseAddress!
}
Note: Swift 5 doesn't allow you to access encodedStringData from within the withUnsafeMutableBytes block! Read Swift 5 Exclusivity Enforcement for why.
Capturing the pointer outside of the block is apparently not recommended, it works but the behavior can get to be undefined in the future
Old answer:
This will help someone looking for getting to the underlying raw bytes (in a UnsafeMutablePointer<UInt8> representation) of a Data object as a variable for further use (instead of having to write all of the logic in the withUnsafeMutableBytes block).
var encodedStringData = Data(base64Encoded: "Vmlub2QgaXMgZ3JlYXQh")!
// byte pointer variable used later to decode the base64 encoded Data
let rawPtr: UnsafeMutablePointer<UInt8> = encodedStringData.withUnsafeMutableBytes { (bytePtr: UnsafeMutablePointer<UInt8>) in bytePtr }
let decodedString = String(bytesNoCopy: rawPtr, length: encodedStringData.count, encoding: .utf8, freeWhenDone: false)
print(decodedString, encodedStringData)
Solution using NSData
let data = NSData(bytes: arrayOfUInt8, length: arrayOfUInt8.count)
let pointer: UnsafeMutablePointer<Int8> = data.bytes.assumingMemoryBound(to: UInt8.self)
Incase of android everything is working perfectly. I want to implement same feature in iOS too but getting different values. Please check the description with images below.
In Java/Android Case:
I tried to convert the string to base64 byte array in java like
byte[] data1 = Base64.decode(balance, Base64.DEFAULT);
Output:
In Swift3/iOS Case:
I tried to convert the string to base64 byte array in swift like
let data:Data = Data(base64Encoded: balance, options: NSData.Base64DecodingOptions(rawValue: 0))!
let data1:Array = (data.bytes)
Output:
Finally solved:
This is due to signed and unsigned integer, meaning unsigned vs signed (so 0 to 255 and -127 to 128). Here, we need to convert the UInt8 array to Int8 array and therefore the problem will be solved.
let intArray = data1.map { Int8(bitPattern: $0) }
In no case should you try to compare data on 2 systems the way you just did. That goes for all types but specially for raw data.
Raw data are NOT presentable without additional context which means any system that does present them may choose how to present them (raw data may represent some text in UTF8 or some ASCII, maybe jpeg image or png or raw RGB pixel data, it might be an audio sample or whatever). In your case one system is showing them as a list of signed 8bit integers while the other uses 8bit unsigned integers for the same thing. Another system might for instance show you a hex string which would look completely different.
As #Larme already mentioned these look the same as it is safe to assume that one system uses signed and the other unsigned values. So to convert from signed (Android) to unsigned (iOS) you need to convert negative values as unsigned = 256+signet so for instance -55 => 256 + (-55) = 201.
If you really need to compare data in your case it is the best to save them into some file as raw data. Then transfer that file to another system and compare native raw data to those in file to check there is really a difference.
EDIT (from comment):
Printing raw data as a string is a problem but there are a few ways. The thing is that many bytes are not printable as strings, may be whitespaces or some reserved codes but mostly the problem is that value of 0 means the end of string in most cases which may exist in the middle of your byte sequence.
So you already have 2 ways of printing byte by byte which is showing Int8 or Uint8 corresponding values. As described in comment converting directly to string may not work as easy as
let string = String(data: data, encoding: .utf8) // Will return nil for strange strings
One way of converting data to string may be to convert each byte into a corresponding character. Check this code:
let characterSequence = data.map { UnicodeScalar($0) } // Create an array of characters from bytes
let stringArray = characterSequence.map { String($0) } // Create an array of strings from array of characters
let myString = stringArray.reduce("", { $0 + $1 }) // Convert an array of strings to a single string
let myString2 = data.reduce("", { $0 + String(UnicodeScalar($1)) }) // Same thing in a single line
Then to test it I used:
let data = Data(bytes: Array(0...255)) // Generates with byte values of 0, 1, 2... up to 255
let myString2 = data.reduce("", { $0 + String(UnicodeScalar($1)) })
print(myString2)
The printing result is:
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
Then another popular way is using a hex string. It can be displayed as:
let hexString = data.reduce("", { $0 + String(format: "%02hhx",$1) })
print(hexString)
And with the same data as before the result is:
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
I hope this is enough but in general you could do pretty much anything with array of bytes and show them. For instance you could create an image treating bytes as RGB 8-bit per component if it would make sense. It might sound silly but if you are looking for some patterns it might be quite a witty solution.
I'm writing a file to disk and I am in the process of converting my code to Swift 3, and got stuck on something. Wanted to see if someone could give me a push in the right direction.
My current code block is:
open let text: NSString
data = possibleData ?? Data()
open let fileURL: URL?
open let fileEncoding: String.Encoding?
fileprivate let data: Data!
text = NSString(bytesNoCopy: UnsafeMutableRawPointer(mutating: data.bytes.bindMemory(to: Void.self, capacity: data.count)), length: data.count, encoding: encoding.rawValue, freeWhenDone: false)!
Swift is saying that calling data.bytes is unavailable and to I need to use .unsafebytes instead. I don't grasp the way you invoke unsafe bytes (it's not as simple as switching bytes to unsafe bytes)
So I did a little research and some people have said to use a closure block like this:
data.withUnsafeMutableBytes {(bytes: UnsafeMutablePointer<UInt8>)->Void in
//work with bytes in here
}
My problem is, what do I put inside the closure block to get my above code working? I think I'm missing something fundamentally. I can't use bytes because it gives the same error again.
Anyone have any ideas? thanks!
If you really need to use this pattern, assuming data was a var and not a let, you could do something like:
let text = data.withUnsafeMutableBytes { bytes in
return NSString(bytesNoCopy: bytes, length: data.count, encoding: encoding.rawValue, freeWhenDone: false)!
}
Or, I don't know why you'd use NSString, so you could do:
let text = data.withUnsafeMutableBytes { bytes in
return String(bytesNoCopy: bytes, length: data.count, encoding: encoding, freeWhenDone: false)!
}
Frankly, this whole pattern seems fragile (why forced cast? why using unsafe pointers rather than various safe patterns? etc.). If you're writing this to a file, so why wouldn't you just write the Data directly?
do {
try data.write(to: fileURL)
} catch {
print("Error: \(error.localizedDescription)")
}
I have the code:
let data = Data(bytes: UnsafePointer<UInt8>(audioBuffer.mData), count: Int(bufferSize))
and
let u16 = UnsafePointer<Int32>(audioBuffer.mData).pointee
Both of which work in Swift 2.3 but not in Swift 3. How do I convert them so they act equivalently? (and why?)
To read 16-bit audio samples from Audio Unit callback buffers in Swift 3, I use:
let bufferPointer = UnsafeMutableRawPointer(mBuffers.mData)
if var bptr = bufferPointer {
for i in 0..<(Int(frameCount)) {
let oneSampleI16 = bptr.assumingMemoryBound(to: Int16.self).pointee
// do something with the audio sample
bptr += 1
}
}
The rest of the Audio Session and Audio Unit code is in this gist: https://gist.github.com/hotpaw2/630a466cc830e3d129b9
I can't say I understand this well, nor have I read the document, but it looks like swift3 pointer casts are scoped to avoid or limit aliasing, so you can't (easily) have two different views of the same piece of memory, or at least not for very long. This means you must either copy the cast data out or do whatever you need to do within a cast callback.
Why eliminate aliasing? I guess it makes for happier compilers.
For Data:
// [NS]Data. probably copying the data
Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
For numeric arrays:
// cast the data to Int32s & (optionally) copy the data out
let umpInt32 = audioBuffer.mData!.assumingMemoryBound(to: Int32.self)
let frameCount = Int(audioBuffer.mDataByteSize/4)
var u32 = [Int32](repeating: 0, count: frameCount)
// copy data from buffer
u32.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: umpInt32, count: frameCount)
}
p.s. there's some confusion in your code. is u16 supposed to be an array of Int32s? Or UInt16s? Or something else?
Check the latest reference of Data.init(bytes:count:).
The type of the parameter bytes is UnsafeRawPointer, which accepts UnsafeMutableRawPointer. And the type of AudioBuffer.mData is UnsafeMutableRawPointer?. You have no need to convert using initializer.
let data = Data(bytes: audioBuffer.mData!, count: Int(bufferSize))
(You just need to explicitly unwrap mData, as it is imported as nullable type, UnsafeMutableRawPointer?, but you need to pass non-nil UnsafeRawPointer (or UnsafeMutableRawPointer).
The second example, you'd better check what sort of methods are available for UnsafeMutableRawPointer. You can find load(fromByteOffset:as:) method, and can use it like this.
let i32 = audioBuffer.mData!.load(as: Int32.self)
`load(