Convert UTF-8 encoded NSData to NSString - ios

I have UTF-8 encoded NSData from windows server and I want to convert it to NSString for iPhone. Since data contains characters (like a degree symbol) which have different values on both platforms, how do I convert data to string?

If the data is not null-terminated, you should use -initWithData:encoding:
NSString* newStr = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
If the data is null-terminated, you should instead use -stringWithUTF8String: to avoid the extra \0 at the end.
NSString* newStr = [NSString stringWithUTF8String:[theData bytes]];
(Note that if the input is not properly UTF-8-encoded, you will get nil.)
Swift variant:
let newStr = String(data: data, encoding: .utf8)
// note that `newStr` is a `String?`, not a `String`.
If the data is null-terminated, you could go though the safe way which is remove the that null character, or the unsafe way similar to the Objective-C version above.
// safe way, provided data is \0-terminated
let newStr1 = String(data: data.subdata(in: 0 ..< data.count - 1), encoding: .utf8)
// unsafe way, provided data is \0-terminated
let newStr2 = data.withUnsafeBytes(String.init(utf8String:))

You could call this method
+(id)stringWithUTF8String:(const char *)bytes.

I humbly submit a category to make this less annoying:
#interface NSData (EasyUTF8)
// Safely decode the bytes into a UTF8 string
- (NSString *)asUTF8String;
#end
and
#implementation NSData (EasyUTF8)
- (NSString *)asUTF8String {
return [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
}
#end
(Note that if you're not using ARC you'll need an autorelease there.)
Now instead of the appallingly verbose:
NSData *data = ...
[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
You can do:
NSData *data = ...
[data asUTF8String];

The Swift version from String to Data and back to String:
Xcode 10.1 • Swift 4.2.1
extension Data {
var string: String? {
return String(data: self, encoding: .utf8)
}
}
extension StringProtocol {
var data: Data {
return Data(utf8)
}
}
extension String {
var base64Decoded: Data? {
return Data(base64Encoded: self)
}
}
Playground
let string = "Hello World" // "Hello World"
let stringData = string.data // 11 bytes
let base64EncodedString = stringData.base64EncodedString() // "SGVsbG8gV29ybGQ="
let stringFromData = stringData.string // "Hello World"
let base64String = "SGVsbG8gV29ybGQ="
if let data = base64String.base64Decoded {
print(data) // 11 bytes
print(data.base64EncodedString()) // "SGVsbG8gV29ybGQ="
print(data.string ?? "nil") // "Hello World"
}
let stringWithAccent = "Olá Mundo" // "Olá Mundo"
print(stringWithAccent.count) // "9"
let stringWithAccentData = stringWithAccent.data // "10 bytes" note: an extra byte for the acute accent
let stringWithAccentFromData = stringWithAccentData.string // "Olá Mundo\n"

Sometimes, the methods in the other answers don't work. In my case, I'm generating a signature with my RSA private key and the result is NSData. I found that this seems to work:
Objective-C
NSData *signature;
NSString *signatureString = [signature base64EncodedStringWithOptions:0];
Swift
let signatureString = signature.base64EncodedStringWithOptions(nil)

Just to summarize, here's a complete answer, that worked for me.
My problem was that when I used
[NSString stringWithUTF8String:(char *)data.bytes];
The string I got was unpredictable: Around 70% it did contain the expected value, but too often it resulted with Null or even worse: garbaged at the end of the string.
After some digging I switched to
[[NSString alloc] initWithBytes:(char *)data.bytes length:data.length encoding:NSUTF8StringEncoding];
And got the expected result every time.

With Swift 5, you can use String's init(data:encoding:) initializer in order to convert a Data instance into a String instance using UTF-8. init(data:encoding:) has the following declaration:
init?(data: Data, encoding: String.Encoding)
Returns a String initialized by converting given data into Unicode characters using a given encoding.
The following Playground code shows how to use it:
import Foundation
let json = """
{
"firstName" : "John",
"lastName" : "Doe"
}
"""
let data = json.data(using: String.Encoding.utf8)!
let optionalString = String(data: data, encoding: String.Encoding.utf8)
print(String(describing: optionalString))
/*
prints:
Optional("{\n\"firstName\" : \"John\",\n\"lastName\" : \"Doe\"\n}")
*/

Related

How to get Bytes from swift Data

in my app I've to create some packet to send directly via network to an external device. I'm trying to create the packet.
In Objective-C I made something like this:
NSString *string = #"0021";
NSData *lengthData = [string dataUsingEncoding:NSNonLossyASCIIStringEncoding];
[data appendBytes:[lengthData bytes] length:[lengthData length]];
so in swift I made something like this:
let string = "0021"
let lengthData = string.data(using: String.Encoding.nonLossyASCII, allowLossyConversion: false)
data.append(lengthData?.bytes, count: pkt.length)
but the compiler says Cannot convert value of type 'UnsafeRawPointer?' to expected argument type 'UnsafePointer<UInt8>', can someone help me to fix this?
Thank you
In Swift you can just append the data (assuming data is Data and var)
if let lengthData = string.data(using: .nonLossyASCII) {
data.append(lengthData)
}

Converting any UTF-8 encoded Characters in String in Swift from API [duplicate]

This question already has answers here:
How do I decode HTML entities in Swift?
(23 answers)
Closed 6 years ago.
I am making an app for checking grades and assignments for my school. From the web when looking at an assignment you see this:
But the server actually returns a string containing both regular characters while the Chinese characters stay in the regular UTF-8 Encoded form:
How would I parse through the raw String in Swift and decode any UTF-8 encoded characters. . i am having a hard time trying to find and even figure out a solution for this online. Just an FYI i cannot change anything on the backend side.
You can use NSAttributedString to convert these HTML entities to string.
let htmlString = "test北京的test"
if let htmldata = htmlString.dataUsingEncoding(NSUTF8StringEncoding), let attributedString = try? NSAttributedString(data: htmldata, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) {
let finalString = attributedString.string
print(finalString)
//output: test北京的test
}
If you just need to convert only numeric entities, you can use CFStringTransform(_:_:_:_:).
Declaration
func CFStringTransform(_ string: CFMutableString!,
_ range: UnsafeMutablePointer<CFRange>!,
_ transform: CFString!,
_ reverse: Bool) -> Bool
...
transform
A CFString object that identifies the transformation to apply. For a
list of valid values, see Transform Identifiers for CFStringTransform.
In macOS 10.4 and later, you can also use any valid ICU transform ID
defined in the ICU User Guide for Transforms.
(Code tested in Swift 3/Xcode 8, iOS 8.4 simulator.)
func decodeNumericEntities(_ input: String) -> String {
let nsMutableString = NSMutableString(string: input)
CFStringTransform(nsMutableString, nil, "Any-Hex/XML10" as CFString, true)
return nsMutableString as String
}
print(decodeNumericEntities("from 北京")) //->from 北京
Or if you prefer computed property and extension:
extension String {
var decodingNumericEntities: String {
let nsMutableString = NSMutableString(string: self)
CFStringTransform(nsMutableString, nil, "Any-Hex/XML10" as CFString, true)
return nsMutableString as String
}
}
print("from 北京".decodingNumericEntities) //->from 北京
Remember these codes above do not work for named character entities, such as > or &.
(From this thread in スタック・オーバーフロー(Japanese StackOverflow).)
You have a handful of HTML/XML entities. You can convert them into "normal text" like this:
// Class declaration in ViewController.h
#interface ViewController : UIViewController <NSXMLParserDelegate>
// Implementation of methods in ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
NSString *xml = #"<root>北</root>";
NSData *data = [NSData dataWithBytes:[xml UTF8String] length:[xml length]];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSLog(#"string: %#", string);
}
The log output is:
string: 北

How to export .JSON file using NSMutableDictionary?

I have NSMutableDictionary inside this values are present like this
{
0 = {
imageangle = "0.09630692";
imageheight = "129.0245";
imagepath = "assets-library://asset/asset.PNG?id=BD849AC4-EDF2-4E05-B5F8-F5EE34385A97&ext=PNG";
imagescalex = "85.48777";
imagescaley = "85.48777";
imagewidth = "129.0245";
mainheight = 282;
mainwidth = 316;
};
memes = {
alpha = 1;
alphaFont = 1;
blue = 1;
blueFont = 0;
green = 1;
greenFont = "0.5";
memestext = "Kishore kumar kumar ";
red = 0;
redFont = 1;
textheight = 34;
textscalex = 13;
textscaley = "30.5";
textwidth = 316;
};
}
Using this i like to export this dictionary in .json file.
After that i am converting into JSONSTRING:
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dickeyjson options:NSJSONWritingPrettyPrinted error:&error];
NSString* aStr;
aStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
My Question:
Is it possible to export it as .json and store it to local storage?
You certainly can export a string to a .json file, and this would be an outline of a correctly error handling way to do so:
do {
let jsonString = "{\"what\":\"ever\"}"
let jsonFile = "file.json"
guard let docs = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first else {
throw NSError(domain: "what-no-Documents-folder??", code: 1, userInfo: nil)
}
let path = NSURL(fileURLWithPath: docs).URLByAppendingPathComponent(jsonFile)
try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
} catch {
fatalError("Writing failed!")
}
and to read it back in,
do {
let path = NSURL(fileURLWithPath: "same as for writing")
let jsonString = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding)
}
catch {
fatalError("Reading failed!")
}
However, it is probable that you want to put this JSON back into a dictionary when reading, yes? In that case, there's no need to convert to JSON, you should just use NSDictionary's writeToURL method directly, using the same outline as above; as I don't believe there's any JSON-representable object not conforming to NSCoder and therefore archivable with writeToFile/writeToURL.
What exactly is the problem? You convert the dictionary to NSData using NSJSONSerialization. Then you write the NSData to a file using a variant of writeToFile: or writeToURL: There is no reason to go through NSString, it's just a waste of time, memory, and battery life.
To read the data, use dataWithContentsOfFile or dataWithContentsOfURL, and again NSJSONSerialization. If you use a mapped file, the NSData doesn't even use memory; that would be useful if your data is multiple megabytes.

Decoding string in base 64

I need to decode a base 64 string. To do so I use the folowing code :
// Get the base 64 string vector.
NSString *vector64String = insertRequestDictionnary[#"check"];
// Decode the base 64 string into data.
NSData *vector64Data = [NSData dataFromBase64String: vector64String];
// Get the string from decoded data.
NSString *decodeVector = [[NSString alloc] initWithData: vector64Data
encoding: NSUTF8StringEncoding];
..But every time i get a nil string (decodeVector).
I check with this website (http://www.base64decode.org/), my first string (vector64string) is base 64. For example : "h508ILubppN1xLpmXWLfnw==" gives "< uĺf]bߟ"
Thanks.
Not all data is an NSUTF8String. The point of Base64 to to create a string representation of data that is not naturally a string.
NSString *vector64String = #"h508ILubppN1xLpmXWLfnw==";
NSData *vector64Data = [[NSData alloc] initWithBase64EncodedString:vector64String options:0];
NSLog(#"vector64Data: %#", vector64Data);
NSLog output:
vector64Data: <879d3c20 bb9ba693 75c4ba66 5d62df9f>
vector64Data is the decoded base64 string.
The vector64Data is not a UTF8 string, it is just data.

JSON Processing in iOS: Removing newlines

I am trying to decode json being sent to my application from Twitter's Streaming API.
Twitter will send my client data which has the following structure:
The body of a streaming API response consists of a series of newline-delimited messages, where "newline" is considered to be \r\n (in hex, 0x0D 0x0A) and "message" is a JSON encoded data structure or a blank line.
They basically separate tweets with \r\n. Sometimes they send you one tweet, sometimes they send you many at once.
Decoding the 'many' case is problematic for me. I use NSJSONSerialization class built into iOS. The problem is that it reads the first tweet and then see's the \r\n and considers this as garbage and returns an empty array and accompanying error.
To get around this problem, I use the following code to basically remove the newlines.
// json is the received json data
NSString* responseString = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding];
NSMutableArray* responseMutableArray = [[responseString componentsSeparatedByString:#"\r\n"] mutableCopy];
[responseMutableArray removeLastObject];
NSArray* responseArray = [NSArray arrayWithArray:responseMutableArray];
[responseMutableArray removeAllObjects];
NSError* error;
for(NSString* tweetString in responseArray)
{
NSDictionary* dict = [NSJSONSerialization JSONObjectWithData:[tweetString dataUsingEncoding:NSUTF8StringEncoding] options: NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
[responseMutableArray addObject:dict];
}
//responseMutableArray contains the parsed tweets
The approach is, convert the data into a NSString, NSString into an array (by spliting by \r\n), array back into data and then parse the JSON into a dictionary. Its a messy process and I wonder if there is something more efficient way that I can do this.
Thanks very much
Vb
Disclaimer: I actually got here by encountering this issue myself.
I think the simplest solution would be a revision of #Elmundo 's answer that removes the newlines completely. It worked for me.
Note: this is Swift version 3.something
guard var string = String(data: data, encoding: .utf8) else {
return
}
string = string.stringByReplacingOccurrencesOfString("\r\n", withString: "")
guard let data = string.data(using: .utf8) else {
return false
}
do {
let json = try NSJSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
}catch let e as NSError {
print(e.description)
}
Try this:
NSString *string = [[NSString alloc] initWithData:json encoding:NSASCIIStringEncoding]:
string = [NSString stringWithFormat:#"[%#]", [string stringByReplacingOccurrencesOfString:#"\\r\\n" withString:#","]];
NSArray *array= [NSJSONSerialization JSONObjectWithData:[string dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
Touch this:
let stringData = String(data: data, encoding: NSUTF8StringEncoding)
if let strData = stringData {
let newString = strData.stringByReplacingOccurrencesOfString("\r\n", withString: "\\r\\n")
if let newString = newString {
do {
let dict:[AnyObject] = try NSJSONSerialization.JSONObjectWithData(newString.dataUsingEncoding(NSUTF8StringEncoding)!, options: [.AllowFragments, .MutableContainers]) as! [AnyObject]
}catch let e as NSError {
print(e.description)
}
}
}

Resources