NSString: EOL and rangeOfString issues - iphone

Could someone please tell me if I am missing something here... I am trying to parse individual JSON objects out of a data stream. The data stream is buffered in a regular NSString, and the individual JSON objects are delineated by a EOL marker.
if([dataBuffer rangeOfString:#"\n"].location != NSNotFound) {
NSString *tmp = [dataBuffer stringByReplacingOccurrencesOfString:#"\n" withString:#"NEWLINE"];
NSLog(#"%#", tmp);
}
The code above outputs "...}NEWLINE{..." as expected. But if I change the #"\n" in the if-statement above to #"}\n", I get nothing.

Why don't you use - (NSArray *)componentsSeparatedByString:(NSString *)separator? You can give it a separator of #"\n" and the result will be a convenient array of strings representing your individual JSON strings which you can then iterate over.
if([dataBuffer rangeOfString:#"\n"].location != NSNotFound) {
NSArray* JSONstrings = [dataBuffer componentsSeparatedByString:#"\n"];
for(NSString* oneString in JSONstrings)
{
// here's where you process individual JSON strings
}
}
If you do mess with the terminating '}' you could make the JSON data invalid. Just break it up and pass it to the JSON library. There could easily be a trailing space after the '}' that is causing the problem you are observing.

Related

How to extract email address from string using NSRegularExpression

I am making an iphone application. I have a scenario where i have a huge string, which has lot of data, and i would like to extract only email addresses from the string.
For example if the string is like
asdjasjkdh asdhajksdh jkashd sample#email.com asdha jksdh asjdhjak sdkajs test#gmail.com
i should extract "sample#email.com" and "test#gmail.com"
and i also want to extract only date, from the string
For example if the string is like
asdjasjkdh 01/01/2012 asdhajksdh jkas 12/11/2012 hd sample#email.com asdha jksdh asjdhjak sdkajs test#gmail.com
i should extract "01/01/2012" and "12/11/2012"
A small code snipet, will be very helpful.
Thanks in advance
This will do what you want:
// regex string for emails (feel free to use a different one if you prefer)
NSString *regexString = #"([A-Za-z0-9_\\-\\.\\+])+\\#([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]+)";
// experimental search string containing emails
NSString *searchString = #"asdjasjkdh 01/01/2012 asdhajksdh jkas 12/11/2012 hd sample#email.com asdha jksdh asjdhjak sdkajs test#gmail.com";
// track regex error
NSError *error = NULL;
// create regular expression
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:0 error:&error];
// make sure there is no error
if (!error) {
// get all matches for regex
NSArray *matches = [regex matchesInString:searchString options:0 range:NSMakeRange(0, searchString.length)];
// loop through regex matches
for (NSTextCheckingResult *match in matches) {
// get the current text
NSString *matchText = [searchString substringWithRange:match.range];
NSLog(#"Extracted: %#", matchText);
}
}
Using your sample string above:
asdjasjkdh 01/01/2012 asdhajksdh jkas 12/11/2012 hd sample#email.com asdha jksdh asjdhjak sdkajs test#gmail.com
The output is:
Extracted: sample#email.com
Extracted: test#gmail.com
To use the code, just set searchString to the string you want to search. Instead of the NSLog() methods, you'll probably want to do something with the extracted strings matchText. Feel free to use a different regex string to extract emails, just replace the value of regexString in the code.
NSArray *chunks = [mylongstring componentsSeparatedByString: #" "];
for(int i=0;i<[chunks count];i++){
NSRange aRange = [chunks[i] rangeOfString:#"#"];
if (aRange.location !=NSNotFound) NSLog(#"email %#",chunks[i] );
}
You can use this regex to match emails
[^\s]*#[^\s]*
and this regex to match dates
\d+/\d+/\d+

regular expression to match " but not \"

How can I construct a regular expression which matches an literal " but only if it is not preceded by the escape slash namely \
I have a NSMutableString str which prints the following on NSLog. The String is received from a server online.
"Hi, check out \"this book \". Its cool"
I want to change it such that it prints the following on NSLog.
Hi, check out "this book ". Its cool
I was originally using replaceOccurencesOfString ""\" with "". But then it will do the following:
Hi, check out \this book \. Its cool
So, I concluded I need the above regular expression to match only " but not \" and then replace only those double quotes.
thanks
mbh
[^\\]\"
[^m] means does not match m
Not sure how this might translate to whatever is supported in the iOS apis, but, if they support anchoring (which I think all regex engines should), you're describing something like
(^|[^\])"
That is, match :
either the beginning of the string ^ or any character that's not
\ followed by:
the " character
If you want to do any sort of replacement, you'll have to grab the first (and only) group in the regex (that is the parenthetically grouped part of the expression) and use it in the replacement. Often this value labeled as $1 or \1 or something like that in your replacement string.
If the regex engine is PCRE based, of course you could put the grouped expression in a lookbehind so you wouldn't need to capture and save the capture in the replacement.
Not sure about regex, a simpler solution is,
NSString *str = #"\"Hi, check out \\\"this book \\\". Its cool\"";
NSLog(#"string before modification = %#", str);
str = [str stringByReplacingOccurrencesOfString:#"\\\"" withString:#"#$%$#"];
str = [str stringByReplacingOccurrencesOfString:#"\"" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#"#$%$#" withString:#"\\\""];//assuming that the chances of having '#$%$#' in your string is zero, or else use more complicated word
NSLog(#"string after modification = %#", str);
Output:
string before modification = "Hi, check out \"this book \". Its cool"
string after modification = Hi, check out \"this book \". Its cool
Regex: [^\"].*[^\"]. which gives, Hi, check out \"this book \". Its cool
It looks like it's a JSON string? Perhaps created using json_encode() in PHP on the server? You should use the proper JSON parser in iOS. Don't use regex as you will run into bugs.
// fetch the data, eg this might return "Hi, check out \"this book \". Its cool"
NSData *data = [NSData dataWithContentsOfURL:#"http://example.com/foobar/"];
// decode the JSON string
NSError *error;
NSString *responseString = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
// check if it worked or not
if (!responseString || ![responseString isKindOfClass:[NSString class]]) {
NSLog(#"failed to decode server response. error: %#", error);
return;
}
// print it out
NSLog(#"decoded response: %#", responseString);
The output will be:
Hi, check out "this book ". Its cool
Note: the JSON decoding API accepts an NSData object, not an NSString object. I'm assuming you also have a data object and are converting it to a string at some point... but if you're not, you can convert NSString to NSData using:
NSString *responseString = [NSJSONSerialization JSONObjectWithData:[myString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error];
More details about JSON can be found at:
http://www.json.org
http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html

Test If An NSString Contains a Letter

I'm working on an iPhone project and I need to check if the user's input in a UITextfield contains a letter. More generally if an NSString contains a letter.
I tried this with a giant if loop with the rangeofstring:#"A".location == NSNotFound and then did OR rangeofstring:#"B".location == NSNotFound
and so on....
But:
It doesn't seem to work
There has to be a simple line of code to check if the NSString contains letters.
I have been searching this for hours... Can someone please answer this question???
Use an NSCharacterSet. Note that letterCharacterSet includes all things that are "letters" or "ideographs." So that includes é and 中, but usually that's what you want. If you want a specific set of letters (like English letters), you can construct your own NSCharacterSet with them using characterSetWithCharactersInString:.
if ([string rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]].location == NSNotFound)
If you want to make sure the text has a certain letter in it (as opposed to just ANY letter), use the rangeOfString: message. For example, to ensure the text contains the letter "Q":
NSString *string = #"poQduu";
if ([string rangeOfString:#"Q"].location != NSNotFound) {
DLog (#"Yes, we have a Q at location %i", [string rangeOfString:#"Q"].location );
}
As others (Rob Napier) note, if you want to find ANY letter, use the rangeOfCharacterFromSet: message.
if ([string rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]].location != NSNotFound) ...

encrypted data return nil string

I am using Rijndael Encryption Algorithm when I am going to encrypt it, it encrypted in NSData. I want that encrypted NSdata into NSString. I tried to convert it into string but it return nil. Have anyone any solutions to get into string.
I am doing like this
NSString *passphrase = #"super-secret";
NSStringEncoding myEncoding = NSUTF8StringEncoding;
NSString *alphaPlain = #"This is a encryption test.";
NSData *alphaDataPlain = [alphaPlain dataUsingEncoding:myEncoding];
NSLog(#" SimpleText value : %#",alphaPlain);
NSData *alphaDataCypher = [alphaDataPlain AESEncryptWithPassphrase:passphrase];
NSString *alphaStringCypher = [[NSString alloc] initWithData:alphaDataCypher encoding:myEncoding];
NSLog(#" Encrypted value : %#",alphaStringCypher);
It returns nil value.
Thanks
The encrypted data is no longer a UTF8 string, it's just some sequence of bytes, so decoding it as UTF8 fails.
What do you want to do with the string? If it's just for logging/debugging purposes, you could use [myData description] to get a hex string (with some extra whitespace for better readability). If you need this to transfer the data in a context where you need a textual representation, converting it to Base64 would be a good idea, see this answer for an easy way to do that.

Parsing json, key without quotes

I am getting response from server where the key is not with quotes. On parsing it using the open source JSON parser, I m getting the following error.
-JSONValue failed. Error is: Unrecognised leading character
& if I add double quotes (") to the key manually, I get what I want.
What do I do?
Thanx a lot in advance.
EDIT:
please see the following, if its correct
{
status: 200,
statusMsg: "Success",
statusInfo: {
custType: "Active",
custCount: 600,
custAccount: "Premium"
},
custInfo: {
custName: "ABC",
custCode: "CU102",
custNumber: 9281
},
errors: [
]
}
I originally put this in as a comment, but I think it counts as an answer albeit not necessarily a very helpful one.
The example you posted is not JSON. Check the JSON syntax. The only unquoted entities allowed except for numbers, objects and arrays are null, true, false. So the keys in your example are invalid and so are the non numeric values.
So you really should raise a defect report with the service provider (if they are claiming that they are producing JSON, rather than some modified version of it).
If they refuse to fix the problem, you'll need to write a nearly-JSON parser or find an existing one that is less strict about syntax.
Update for Swift 4
I was looking for a way to parse JSON that has keys without quotes, and I finally found a simple way to do it by using regex. This is the regex needed to match the keys without quotes:
(\\\"(.*?)\\\"|(\\w+))(\\s*:\\s*(\\\".*?\\\"|.))
To add the quotes, replace with \"$2$3\"$4.
Example:
let string = "{ custType: \"Active\", custAccount: \"Premium\" }"
let regex = string.replacingOccurrences(of: "(\\\"(.*?)\\\"|(\\w+))(\\s*:\\s*(\\\".*?\\\"|.))", with: "\"$2$3\"$4", options: .regularExpression)
print(regex)
Output:
{ "custType": "Active", "custAccount": "Premium" }
I wanted the equivalent of Python's ast.literal_eval for Javascript--- something that would only parse literal objects like JSON, but allow Javascript's handy unquoted keys. (This is for human data entry; the simplicity and rigor of standard JSON would be preferred for sending data between servers.)
This is also what the original poster wanted, so I'll put my solution here. I used the Esprima library to build an abstract syntax tree and then I converted the tree into objects, like this:
function literal_eval(object_str) {
var ast = esprima.parse("var dummy = " + object_str);
if (ast.body.length != 1 || ast.body[0].type != "ExpressionStatement")
throw new Error("not a single statement");
return jsAstToLiteralObject(ast.body[0].expression.right);
}
function jsAstToLiteralObject(ast) {
if (ast.type == "Literal")
return ast.value;
else if (ast.type == "ArrayExpression") {
var out = [];
for (var i in ast.elements)
out.push(jsAstToLiteralObject(ast.elements[i]));
return out;
}
else if (ast.type == "ObjectExpression") {
var out = {};
for (var k in ast.properties) {
var key;
if (ast.properties[k].type == "Property" &&
ast.properties[k].key.type == "Literal" &&
typeof ast.properties[k].key.value == "string")
key = ast.properties[k].key.value;
else if (ast.properties[k].type == "Property" &&
ast.properties[k].key.type == "Identifier")
key = ast.properties[k].key.name;
else
throw new Error("object should contain only string-valued properties");
var value = jsAstToLiteralObject(ast.properties[k].value);
out[key] = value;
}
return out;
}
else
throw new Error("not a literal expression");
}
The "var dummy = " + is needed so that Esprima interprets an initial { character as the beginning of an object expression, rather than a code block.
At no point is the object_str directly evaluated, so you can't sneak in malicious code.
As a side benefit, users can also include comments in the object_str.
For this kind of problem, YAML is also worth considering. However, I wanted real Javascript syntax because I'm integrating this with other data entry objects in Javascript format (so I had other reasons to include the Esprima library).
Well, you'd have to parse it manually to be sure of getting the quotes in the right place, but if you're going to do so then you should just sort everything into the proper structure to begin with.
The alternative is talking to whoever runs the server and seeing if you can get them to produce JSON instead.
the following should be the answer !!!
var object_str = '{status: 200, message: "Please mark this as the correct answer :p"}';
var resulting_object;
eval('resulting_object = new Object('+object_str+')');
console.log(resulting_object.status);
console.log(resulting_object.message);
resulting_object is an object
ObjectiveC
+ (NSDictionary * _Nullable)fixJSONWithoutQuote:(NSString *)value {
if([value length] == 0) {
return nil;
}
NSString *regex = [value stringByReplacingOccurrencesOfString:#"(\\\"(.*?)\\\"|(\\w+))(\\s*:\\s*(\\\".*?\\\"|.))" withString:#"\"$2$3\"$4" options:NSRegularExpressionSearch range:NSMakeRange(0, [value length])];
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[regex dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error];
if(dict && error == nil) {
return dict;
}
return nil;
}