Make NSArray out of each word in text file - nsarray

So what I want to accomplish is to make a NSArray out of a txt file and each word would be an object in that array. Below is some code that doesn't work. No crashes but doesn't do anything.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"badwords" ofType:#"txt"];
if (filePath) {
NSString *fileContents = [NSString stringWithContentsOfFile:#"badwords.txt" encoding:NSUTF8StringEncoding error:nil];
lines = [fileContents componentsSeparatedByString:#"\n"];
}
lines is a NSArray.
Any reason why this shouldn't work? Each word is separated by a line break.

A good reason: [NSString stringWithContentsOfFile: ...] should be taking the filePath you derived in the preceding statement as an argument and not #"badwords.txt", the literal string filename. That's the whole reason for deriving the filePath in the first line -- you then want to use it in the second.
In other words I'm simply saying you should change this:
NSString *fileContents = [NSString stringWithContentsOfFile:#"badwords.txt" encoding:NSUTF8StringEncoding error:nil];
to this:
NSString *fileContents = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

Related

Replace a specific word in entire NSString with a special character using reggae

I need to replace a word like DFT with the registered trademark symbol everywhere in a string from a plist.
My code works but always messes up the second one in the same string:
- (NSAttributedString *)addRegisteredTrademarkTo:(NSString*)text
{
NSMutableAttributedString *rawAttString=[[NSMutableAttributedString alloc] initWithString:text];
UIFont *boldFont = [UIFont boldSystemFontOfSize:8];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:#"®"
attributes:#{NSFontAttributeName:boldFont,
NSForegroundColorAttributeName:[UIColor blackColor],
NSBaselineOffsetAttributeName : #4,
NSFontAttributeName:[UIFont fontWithName:#"Helvetica" size:5]
}];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"DRT" options:kNilOptions error:nil];
NSRange range = NSMakeRange(0,rawAttString.length);
[regex enumerateMatchesInString:text options:kNilOptions range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange subStringRange = [result rangeAtIndex:0];
[rawAttString replaceCharactersInRange:subStringRange withAttributedString:attributedString];
}];
NSAttributedString *t = [[NSAttributedString alloc] initWithAttributedString:rawAttString];
return t;
}
String is:Fake name™ with Another FakeDRT building access. SionDRT elevators size
Output is:
The second symbol is always misplaced - and I can't figure out why. Any pointers?

NSRegularExpression - match multiple string

Basically, my string looks like this:
#"{{attendee.prefix}} {{attendee.firstname}} {{attendee.lastname}}, fwf<br /><span style="font-size:14px;">lalallasgabab {{attendee.weg2g}} {{attendee.5236t2gsg}}  {{attendee.ticket_no}}  agagawfbeagabs</span>"
I am trying to extract all the string which encapsulated by 2 curly braces:
[ {{attendee.prefix}}, {{attendee.firstname}}, {{attendee.lastname}}, {{attendee.weg2g}}, {{attendee.5236t2gsg}}, {{attendee.ticket_no}} ]
I have tried these regex, but it always return 1 match if not the whole string.
#"(\\{\\{.*\\}\\})" -> return the whole string
#"\\{\\{[^}]*+\\}\\}" -> only match {{attendee.firstname}}
#"\\b\\{\\{[^}]*+\\}\\}\\b" -> only match {{attendee.prefix}}
Here is my code:
NSString *myString = #"{{attendee.prefix}} {{attendee.firstname}} {{attendee.lastname}}, fwf<br /><span style="font-size:14px;">lalallasgabab {{attendee.weg2g}} {{attendee.5236t2gsg}}  {{attendee.ticket_no}}  agagawfbeagabs</span>"
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\{\\{[^}]*+\\}\\}" options:NSRegularExpressionCaseInsensitive error:nil];
NSRange visibleTextRange = NSMakeRange(0, myString.length);
NSArray *matches = [regex matchesInString:myString options:NSMatchingAnchored range:visibleTextRange];
for (NSTextCheckingResult *match in matches)
{
NSLog(#"%#: Match - %#", [self class], [myString substringWithRange:match.range]);
}
I have tried using [match rangeAtIndex:index] but still return the same thing, sometime it is out of bound because the match result is only 1.
Appreciate any help here. Thanks.
PS: I am new to Objective-C and RegEx, so pardon this question.
To iterate over all matches like {{this}}, use this:
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\{\\{[^}]*\\}\\}" options:0 error:&error];
NSArray *matches = [regex matchesInString:subject options:0 range:NSMakeRange(0, [subject length])];
NSUInteger matchCount = [matches count];
if (matchCount) {
for (NSUInteger matchIdx = 0; matchIdx < matchCount; matchIdx++) {
NSTextCheckingResult *match = [matches objectAtIndex:matchIdx];
NSRange matchRange = [match range];
NSString *result = [subject substringWithRange:matchRange];
}
}
else { // Nah... No matches.
}
I managed to answer my own question, by using different ways of enumerating the result:
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\{\\{[^}]*+\\}\\}" options:NSRegularExpressionCaseInsensitive error:nil];
NSString *myString = #"{{attendee.prefix}} {{attendee.firstname}} {{attendee.lastname}}, fwf<br /><span style='font-size:14px;'>lalallasgabab {{attendee.weg2g}} {{attendee.5236t2gsg}} {{attendee.ticket_no}} agagawfbeagabs</span>";
[regex enumerateMatchesInString:myString
options:0
range:NSMakeRange(0, [myString length])
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSLog(#"%#: Match - %#", [self class], [self.duplicateBody substringWithRange:result.range]);
}];
With the code above, I am able to iterate through each of the matched string, which is exactly what I wanted.

stringValue of NSDictionary objectForKey runtime error

So I have the following code that causes a runtime error in XCode
NSString* temp = [[param objectforkey#"firstParam"] stringValue];
int tempNum = [[param objectforkey#"secondParam"] intValue];
param loads from a plist. firstParam is as string, secondParam is a number
First line crashes the program.
Now what's interesting is it works if I do a hard caste i.e:
NSString* temp = (NSString*)[param objectforkey#"firstParam"];
int tempNum = [[param objectforkey#"secondParam"] intValue];
Just wondering why the id would have inconsistent implementation in that I have to use intValue to cast to int, but have to do hard cast to get NSString? Why not stringValue?
Your first call to objectForKey returns an object which is an NSString. NSString doesn't have a stringValue method which is why a runtime error is generated. It doesn't make sense to get the stringValue of something that is already a string.
The second call to objectForKey returns an NSNumber. NSNumber does have an intValue method so it doesn't cause an error.
In the second case you are changing the type of the returned value from NSNumber to int. In the first case the object returned is already an NSString and there is no point trying to get the stringValue of a string.
The call [param objectforkey#"firstParam"] DOES return a NSString, so there's no need to call stringValue. Furthermore, NSString does not have a method called "stringValue", so that's the source of your problem.
Just call this: NSString *temp = (NSString *)[param objectForKey:#"firstParam"];
Also note that the selector is objectForKey:, not objectforkey: (capitalization matters).
And, answering your question, NSNumber is a class, so any object is not an "int", but a NSNumber pointer. intValue returns an actual "int", as floatValue returns a "float" (NSNumber can represent any type of number). In the case of the NSString that's not necessary, because there is only one type of NSString (there is, though, a method for returning a const char *, and that would be used like char *string = [[param objectForKey:#"firstParam"] cString];... not very useful for Objective-C apps anyway).
Lets say you call a web service that returns JSON
Then you process data
NSDictionary *json_response = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
now you try something like this
NSString *name = [[object objectForKey:#"Name"] stringValue];
then you get the error....
As mttrb mentions you get the error because of stringValue, objectForKey default return value is String so a potential fix in this case would be:
NSString *name = [object objectForKey:#"Name"];
Hope this hints some one!
If you can not be sure that the returned value is a NSString you should do this:
id value = [param objectForKey#"firstParam"];
if ([value isKindOfClass:[NSString class]]) {
NSString *temp = (NSString *)id;
//Handle string
}
else {
//Handle other case
}
Completing the answer of #Torge, I add my solution to get always an NSString from objectForKey method:
NSObject * value = [param objectForKey#"firstParam"];
NSString *stringValue;
if ([value isKindOfClass:[NSString class]]) {
//Handle string
stringValue = (NSString *)value;
}
else if([value isKindOfClass:[NSNumber class]]){
//Handle NSNumber
stringValue = [((NSNumber *) value) stringValue];
}
else {
//Handle other case
}

Time validation with RegularExpression

I'm new of the Objective-C's code programming.... and is the first time that I post a question over Stackoverflow.
Somebody can say me why this piece of code don't works?
I've tested the Regular Expression on the test web site and work fine, but in Xcode don't works.... I need to validate a time in this format (HH:MM).
Thanks all...
(the code)
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *expression = [[NSString alloc] init];
if (textField == self.TB_orarioPartenza)
{
expression = #"^([0-1][0-9]|[2][0-3]):([0-5][0-9])$";
}
if (textField == self.TB_numGiri) {
expression = #"^d{2}";
}
NSString *newString =[textField.text stringByReplacingCharactersInRange:range withString:string];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression
options:0
error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString
options:0
range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0) return NO;
return YES;
}
For h:mm format (for example "9:35") use use regex:
^(?:0?[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$
For hh:mm format (for example "09:35") use use regex:
^(?:0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$

How do you use NSRegularExpression's replacementStringForResult:inString:offset:template:

I have a series of characters that I want to match with a regular expression, and replace them with specific strings depending on what they are.
Example:
In => "This is the input string where i want to replace 1 2 & 3"
Out => "This is the input string where i want to replace ONE TWO & THREE"
I currently do this by splitting the string using spaces as the separator, and parsing each string individually, incrementally rebuilding the string. I feel this is ugly, and lacking imagination, and kinda slow.
According the Apple documentation I should be able to do this using the replacementStringForResult:inString:offset:template: method. However I can't seem to understand how to use it correctly.
You can use the method within a for in loop using matchesInString:options:range:, which returns an array of matches as NSTextCheckingResults:
NSError* error = NULL;
NSRegularExpression* regex = [NSRegularExpression
regularExpressionWithPattern:#"\\b[1-3]\\b"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSString* yourString = #"This is the input string where i want to replace 1 2 & 3";
NSMutableString* mutableString = [yourString mutableCopy];
NSInteger offset = 0; // keeps track of range changes in the string
// due to replacements.
for (NSTextCheckingResult* result in [regex matchesInString:yourString
options:0
range:NSMakeRange(0, [yourString length])]) {
NSRange resultRange = [result range];
resultRange.location += offset; // resultRange.location is updated
// based on the offset updated below
// implement your own replace functionality using
// replacementStringForResult:inString:offset:template:
// note that in the template $0 is replaced by the match
NSString* match = [regex replacementStringForResult:result
inString:mutableString
offset:offset
template:#"$0"];
NSString* replacement;
if ([match isEqualToString:#"1"]) {
replacement = #"ONE";
} else if ([match isEqualToString:#"2"]) {
replacement = #"TWO";
} else if ([match isEqualToString:#"3"]) {
replacement = #"THREE";
}
// make the replacement
[mutableString replaceCharactersInRange:resultRange withString:replacement];
// update the offset based on the replacement
offset += ([replacement length] - resultRange.length);
}
NSLog(#"mutableString: %#", mutableString); // mutableString: This is the input string where i want to replace ONE TWO & THREE
The answer of Dano works perfectly and following the idea of Pedro in the comments I wrapped the code into a category that takes a block that does the replacement part.
This is very handy to use.
NSRegularExpression+Replacement.h
#interface NSRegularExpression (Replacement)
- (NSString *)stringByReplacingMatchesInString:(NSString *)string
options:(NSMatchingOptions)options
range:(NSRange)range
template:(NSString *)templ
withMatchTransformation: (NSString* (^) (NSString* element))transformation;
#end
NSRegularExpression+Replacement.m
#implementation NSRegularExpression (Replacement)
- (NSString *)stringByReplacingMatchesInString:(NSString *)string
options:(NSMatchingOptions)options
range:(NSRange)range
template:(NSString *)templ
withMatchTransformation: (NSString* (^) (NSString* element))transformation
{
NSMutableString* mutableString = [string mutableCopy];
NSInteger offset = 0; // keeps track of range changes in the string due to replacements.
for (NSTextCheckingResult* result in [self matchesInString:string
options:0
range:range])
{
NSRange resultRange = [result range];
resultRange.location += offset; // resultRange.location is updated based on the offset updated below
// implement your own replace functionality using
// replacementStringForResult:inString:offset:template:
// note that in the template $0 is replaced by the match
NSString* match = [self replacementStringForResult:result
inString:mutableString
offset:offset
template:templ];
// get the replacement from the provided block
NSString *replacement = transformation(match);
// make the replacement
[mutableString replaceCharactersInRange:resultRange withString:replacement];
// update the offset based on the replacement
offset += ([replacement length] - resultRange.length);
}
return mutableString;
}
#end
And here is how you use it to solve the initial question:
NSString* yourString = #"This is the input string where i want to replace 1 2 & 3";
NSError* error = nil;
NSRegularExpression* regex = [NSRegularExpression
regularExpressionWithPattern:#"\\b[1-3]\\b"
options:0
error:&error];
return [regex stringByReplacingMatchesInString:yourString options:0 range:NSMakeRange(0, [yourString length]) template:#"$0" withMatchTransformation:^NSString *(NSString *match) {
NSString* replacement;
if ([match isEqualToString:#"1"]) {
replacement = #"ONE";
} else if ([match isEqualToString:#"2"]) {
replacement = #"TWO";
} else if ([match isEqualToString:#"3"]) {
replacement = #"THREE";
} else {
return replacement;
}
}];
You should use
- (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)template
or
- (NSUInteger)replaceMatchesInString:(NSMutableString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)template
with string being "This is the input string where i want to replace 1 2 & 3" and template being either "ONE", "TWO" or "THREE".
I needed a more generic solution for the same problem so I refined Dano's answer to a method like this, with a sample usage explained below:
- (NSMutableString *)replaceSubstringsInString:(NSString*)string
usingRegex:(NSString*)searchRegex
withReplacements:(NSDictionary*)replacements {
NSMutableString *newString = [string mutableCopy];
__block NSInteger offset = 0;
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:searchRegex
options:0
error:&error];
[regex enumerateMatchesInString:string
options:0
range:NSMakeRange(0, [string length])
usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSRange resultRange = [match range];
resultRange.location += offset;
NSString *substring = [string substringWithRange:match.range];
__block NSString* replacement;
[replacements enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([key isEqualToString:substring]) {
replacement = obj;
}
}];
[newString replaceCharactersInRange:resultRange withString:replacement];
offset += ([replacement length] - resultRange.length);
}];
return newString;
}
Usage:
NSString *string = #"This is the input string where i want to replace 1 2 & 3";
NSString *searchRegex = #"\\b[1-3]\\b";
NSDictionary *replacements = #{#"1":#"ONE",#"2":#"TWO",#"3":#"THREE"};
NSMutableString *result = [self replaceSubstringsInString:string
usingRegex:searchRegex
withReplacements:replacements];
Explanation:
You just have to pass in the string to search for the matching substrings, along with the desired regexSearch pattern and a replacements dictionary containing the string pairs with the key being the substring to be replaced and the object with the desired replacement string.
Output:
// This is the input string where i want to replace ONE TWO & THREE
I was looking for something similar, but didn't like most of the answers here, so I wrote something inspired by how PHP does string replacement:
#implementation NSString (preg_replace)
- (instancetype)stringByReplacingMatchesFromRegularExpression:(NSRegularExpression *)regularExpression replacementBlock:(NSString * (^)(NSArray *matches))replacementBlock
{
NSMutableString *finalString = self.mutableCopy;
NSUInteger offset = 0;
for (NSTextCheckingResult *match in [regularExpression matchesInString:self options:0 range:NSMakeRange(0, self.length)]) {
NSMutableArray *matches = [NSMutableArray array];
for (NSUInteger index = 0; index < match.numberOfRanges; ++index) {
[matches addObject:[self substringWithRange:[match rangeAtIndex:index]]];
}
NSString *replacementString = replacementBlock(matches.copy);
[finalString replaceCharactersInRange:NSMakeRange(match.range.location + offset, match.range.length) withString:replacementString];
offset += replacementString.length - match.range.length;
}
return finalString;
}
#end
To use it:
NSRegularExpression *expression = [[NSRegularExpression alloc] initWithPattern:#"[0-9A-F]{2}" options:0 error:nil];
NSString *string = #"AB-DE-EF";
NSString *result = [string stringByReplacingMatchesFromRegularExpression:expression replacementBlock:^NSString *(NSArray *matches) {
return [NSString stringWithFormat:#"(%#)", [matches[0] lowercaseString]];
}];
NSLog(#"Result = %#", result); // "(ab)-(de)-(ef)"
I recommend this, much shorter and uses a little memory :
Another solution