Objective-C, get string from text file? - objective-c

I know there are a few different ways to find text in file, although I haven't found a way to return the text after the string I'm searching for. For example, if I was to search file.txt for the term foo and wanted to return bar, how would I do that without knowing it's bar or the length?
Here's the code I'm using:
if (!fileContentsString) {
NSLog(#"Error reading file");
}
// Create the string to search for
NSString *search = #"foo";
// Search the file contents for the given string, put the results into an NSRange structure
NSRange result = [fileContentsString rangeOfString:search];
// -rangeOfString returns the location of the string NSRange.location or NSNotFound.
if (result.location == NSNotFound) {
// foo not found. Bail.
NSLog(#"foo not found in file");
return;
}
// Continue processing
NSLog(#"foo found in file");
}

you could use [NSString substringFromIndex:]
if (result.location == NSNotFound)
{
// foo not found. Bail.
NSLog(#"foo not found in file");
return;
}
else
{
int startingPosition = result.location + result.length;
NSString* foo = [fileContentsString substringFromIndex:startingPosition]
NSLog(#"found foo = %#",foo);
}

You might want to use RegexKitLite and perform a regex look up:
NSArray * captures = [myFileString componentsMatchedByRegex:#"foo\\s+(\\w+)"];
NSString * wordAfterFoo = captures[1];
Not test though.

Related

Checking if two loops returns a value

I want to check if the two for loops returns a match, if they do nothing happens but if they don't I want a message to be printed out saying something like "A match could not be found". Something like this:
if (loop1 == 0 && loop2 == 0) {
NSLog(#"A match could not be found, please check your spelling");
};
My question is therefore, how can I describe the loops so that I can check the value they give?
Here are the loops:
for (SMADoc *aSearch in docs) {
if ([search isEqualToString:[aSearch leadAuthour]]) {
//Open the file that is represented by UrlToDoc for that specific object
[[NSWorkspace sharedWorkspace] openFile:[aSearch urlToDoc]];
}
}
} else {
//The string starts with a number and should be converted to an int and then the array of the numbers should be searched through
int number = atoi(searchC);
NSNumber *sNumber = [NSNumber numberWithInt:number];
for (SMADoc *nSearch in docs) {
if ([sNumber isEqualToNumber:[nSearch docNumber]]) {
[[NSWorkspace sharedWorkspace] openFile:[nSearch urlToDoc]];
}
}
}
Thanks in advance!
As your custom class is KVC compliant you could perform the check simultaneously with NSPredicate without any loop.
The expression means: if docs contains an object whose docNumber is number or leadAuthour is search, skip the error message
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"docNumber == %i || leadAuthour == %#", number, search];
if [[docs filteredArrayUsingPredicate:predicate] count] == 0 {
NSLog(#"A match could not be found, please check your spelling");
}
I solved it! I started by declaring two ints, one for each loop, and then checked if a match was NOT found for each iteration, i.e:
![search isEqualToString:[aSearch leadAuthour]]
If this was true, the int was increased by one. Then, after the array was iterated through, if the int is equal to the number of objects in the array, I then knew that none of the objects matched and I could print out the "A match could not be found, please check your spelling"-message. This is how it looked like in code:
int i = 0;
int j = 0;
for (SMADoc *aSearch in docs) {
if ([search isEqualToString:[aSearch leadAuthour]]) {
//Open the file that is represented by UrlToDoc for that specific object
[[NSWorkspace sharedWorkspace] openFile:[aSearch urlToDoc]];
}
if(![search isEqualToString:[aSearch leadAuthour]]){
i++;
}
if(i == [docs count]) {
NSLog(#"A match could not be found, please check your spelling");
}
}
} else {
//The string starts with a number and should be converted to an int and then the array of the numbers should be searched through
int number = atoi(searchC);
NSNumber *sNumber = [NSNumber numberWithInt:number];
for (SMADoc *nSearch in docs) {
if ([sNumber isEqualToNumber:[nSearch docNumber]]) {
[[NSWorkspace sharedWorkspace] openFile:[nSearch urlToDoc]];
}
if(![search isEqualToString:[aSearch docNumber]]){
j++;
}
if(j == [docs count]) {
NSLog(#"A match could not be found, please check your spelling");
}
}
I don't know if it's allowed to answer your own question, but my intention with this was to provide an answer for someone who might be asking the same question.

Matching strings, consider some characters are the same

please help me with this problem.
I want to check if the targetString match the keyword or not. Consider some character may different, but should still return true.
Example:
targetString = #"#ß<"
keyword = #"abc", #"∂B(", #"#Aß<"
result: all must return true.
(Matched.targetString and all keyword are the same.)
Consider me have an array, contains list of character set that can be the same:
NSArray *variants = [NSArray arrayWithObjects:#"aA#∂", #"bBß", #"c©C<(", nil]
So that when matching, with this rule, it can match as the example above.
Here is what i've done so far (using recursion):
- (BOOL) test:(NSString*)aString include:(NSString*) keyWord doTrim:(BOOL)doTrim {
// break recursion.
if([aString length] < [keyWord length]) return false;
// First, loop through each keyword's character
for (NSUInteger i = 0; i < [keyWord length]; i++) {
// Get #"aA#∂", #"bBß", #"c©C<(" or only the character itself.
// like, if the keyword's character is A, return the string #"aA#∂".
// If the character is not in the variants set, eg. P, return #"P"
char c = [keyWord characterAtIndex:i];
NSString *rs = [self variantsWithChar:c];
// Check if rs (#"aA#∂" or #"P") contains aString[i] character
if([rs rangeOfString:[NSString stringWithCharacters:[aString characterAtIndex:i] length:1]].location == NSNotFound) {
// If not the same char, remove first char in targetString (aString), recursion to match again.
return [self test:[aString substringFromIndex:1] include:keyWord doTrim:NO];
}
}
// If all match with keyword, return true.
return true;
}
- (NSString *) variantsWithChar:(char) c {
for (NSString *s in self.variants) {
if ([s rangeOfString:[NSString stringWithFormat:#"%c",c]].location != NSNotFound) {
return s;
}
}
return [NSString stringWithFormat:#"%c", c];
}
The main problem is, variantsWithChar: doesn't return the correct string. I don't know which datatype and which function should I use here. Please help.
For thou who know ruby, here's the example in ruby. It work super fine!
require 'test/unit/assertions'
include Test::Unit::Assertions
class String
def matching?(keyword)
length >= keyword.length && (keyword.chars.zip(chars).all? { |cs| variants(cs[0]).include?(cs[1]) } || slice(1, length - 1).matching?(keyword))
end
private
VARIANTS = ["aA#∂", "bBß", "c©C<("]
def variants(c)
VARIANTS.find { |cs| cs.include?(c) } || c
end
end
assert "abc".matching?("#ß<")
PS: The fact is, it's containt a japanese character set that sounds the same (like あア, いイ... for thou who know japanese)
PS 2: Please feel free to edit this Question, since my engrish is sooo bad. I may not tell all my thought.
PS 3: And, maybe some may comment about the performance. Like, search about 10,000 target words, with nearly 100 variants, each variant have at most 4 more same characters.
So first off, ignore comments about ASCII and stop using char. NSString and CFString use unichar
If what you really want to do is transpose hiragana and katakana you can do that with CFStringTransform()
It wraps the ICU libraries included in OS X and iOS.
It makes it very simple.
Search for that function and you will find examples of how to use it.
After a while (a day) working on the code above, I finally get it through. But don't know about the performance. Someone comment and help me improve about performance, please. Thanks.
- (BOOL) test:(NSString*)aString include:(NSString*) keyWord doTrim:(BOOL)doTrim {
// break recursion.
if([aString length] < [keyWord length]) return false;
// First, loop through each keyword's character
for (NSUInteger i = 0; i < [keyWord length]; i++) {
// Get #"aA#∂", #"bBß", #"c©C<(" or only the character itself.
// like, if the keyword's character is A, return the string #"aA#∂".
// If the character is not in the variants set, eg. P, return #"P"
NSString* c = [NSString stringWithFormat:#"%C", [keyWord characterAtIndex:i]];
NSString *rs = [self variantsWithChar:c];
NSString *theTargetChar = [NSString stringWithFormat:#"%C", [aString characterAtIndex:i]];
// Check if rs (#"aA#∂" or #"P") contains aString[i] character
if([rs rangeOfString:theTargetChar].location == NSNotFound) {
// If not the same char, remove first char in targetString (aString), recursion to match again.
return [self test:[aString substringFromIndex:1] include:keyWord doTrim:NO];
}
}
// If all match with keyword, return true.
return true;
}
If you remove all comment, it'll be pretty short...
////////////////////////////////////////
- (NSString *) variantsWithChar:(NSString *) c{
for (NSString *s in self.variants) {
if ([s rangeOfString:c].location != NSNotFound) {
return s;
}
}
return c;
}
You could try comparing ascii values of the japanese characters in the variants's each character's ascii value. These japanese characters aren't treated like usual characters or string. Hence, string functions like rangeOfString won't work on them.
to be more precise: have a look at the following code.
it will search for "∂" in the string "aA#∂"
NSString *string = #"aA#∂";
NSMutableSet *listOfAsciiValuesOfString = [self getListOfAsciiValuesForString:string]; //method definition given below
NSString *charToSearch = #"∂";
NSNumber *ascii = [NSNumber numberWithInt:[charToSearch characterAtIndex:0]];
int countBeforeAdding = [listOfAsciiValuesOfString count],countAfterAdding = 0;
[listOfAsciiValuesOfString addObject:ascii];
countAfterAdding = [listOfAsciiValuesOfString count];
if(countAfterAdding == countBeforeAdding){ //element found
NSLog(#"element exists"); //return string
}else{
NSLog(#"Doesnt exists"); //return char
}
===================================
-(NSMutableSet*)getListOfAsciiValuesForString:(NSString*)string{
NSMutableSet *set = [[NSMutableSet alloc] init];
for(int i=0;i<[string length];i++){
NSNumber *ascii = [NSNumber numberWithInt:[string characterAtIndex:i]];
[set addObject:ascii];
}
return set;
}

Objective-C String Function: Contains String

How can I check if an NSString contains another substring, at which point it will return a Boolean Value.
This is what I'm thinking of:
If myString.contains("string") then
{
//Stuff Happens
}
But, from the research I've done, it seems as if Obj-C has no function for this. This Wikipedia article gives numerous string functions, their differences, and all in different languages, but I see no Obj-C support for any Contain Function.
Does anyone know of a simple-to-use function like the once above (which is similar to the C# and VB.NET function)?
Would a "Find" Function work? If so, how?
If this is not supported in Obj-C, is there a workaround I can use?
Any help is very appreciated.
if ([myString rangeOfString:#"string"].location != NSNotFound)
{
// Stuff happens
}
NSString *someString = #"Time for an egg hunt";
if ( [someString rangeOfString:#"egg" options:NSCaseInsensitiveSearch].location != NSNotFound ) {
NSLog( #"Found it!" );
}
If you want to be case insensitive.
NSRange range = [string rangeOfString:#"string" options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound)
{
return range.location;
}
else
{
return nil;
}
Documentation
Create a NSString category, and put that in...
Code :
- (BOOL)contains:(NSString *)str
{
NSRange aRange = [self rangeOfString:str];
return (aRange.location!=NSNotFound);
}
Usage :
NSString* testStr = #"This is my string";
if ([testStr contains:#"is"])
{
// do something
}
if([string rangeOfString:substring].length > 0)
...

Insert or split string at uppercase letters objective-c

What would be the most efficient way to convert a string like "ThisStringIsJoined" to "This String Is Joined" in objective-c?
I receive strings like this from a web service thats out of my control and I would like to present the data to the user, so I would just like to tidy it up a bit by adding spaces infront of each uppercase word. The strings are always formatted with each word beginning in an uppercase letter.
I'm quite new to objective-c so cant really figure this one out.
Thanks
One way of achieving this is as follows:
NSString *string = #"ThisStringIsJoined";
NSRegularExpression *regexp = [NSRegularExpression
regularExpressionWithPattern:#"([a-z])([A-Z])"
options:0
error:NULL];
NSString *newString = [regexp
stringByReplacingMatchesInString:string
options:0
range:NSMakeRange(0, string.length)
withTemplate:#"$1 $2"];
NSLog(#"Changed '%#' -> '%#'", string, newString);
The output in this case would be:
'ThisStringIsJoined' -> 'This String Is Joined'
You might want to tweak the regular expression to you own needs. You might want to make this into a category on NSString.
NSRegularExpressions are the way to go, but as trivia, NSCharacterSet can also be useful:
- (NSString *)splitString:(NSString *)inputString {
int index = 1;
NSMutableString* mutableInputString = [NSMutableString stringWithString:inputString];
while (index < mutableInputString.length) {
if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:[mutableInputString characterAtIndex:index]]) {
[mutableInputString insertString:#" " atIndex:index];
index++;
}
index++;
}
return [NSString stringWithString:mutableInputString];
}
Here's a category on NSString that will do what you want. This will handle non-ASCII letters. It will also split "IDidAGoodThing" properly.
#implementation NSString (SeparateCapitalizedWords)
-(NSString*)stringBySeparatingCapitalizedWords
{
static NSRegularExpression * __regex ;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSError * error = nil ;
__regex = [ NSRegularExpression regularExpressionWithPattern:#"[\\p{Uppercase Letter}]" options:0 error:&error ] ;
if ( error ) { #throw error ; }
});
NSString * result = [ __regex stringByReplacingMatchesInString:self options:0 range:(NSRange){ 1, self.length - 1 } withTemplate:#" $0" ] ;
return result ;
}
#end
Here is Swift Code (objective c code by webstersx), Thanks !
var str: NSMutableString = "iLoveSwiftCode"
var str2: NSMutableString = NSMutableString()
for var i:NSInteger = 0 ; i < str.length ; i++ {
var ch:NSString = str.substringWithRange(NSMakeRange(i, 1))
if(ch .rangeOfCharacterFromSet(NSCharacterSet.uppercaseLetterCharacterSet()).location != NSNotFound) {
str2 .appendString(" ")
}
str2 .appendString(ch)
}
println("\(str2.capitalizedString)")
}
Output : I Love Swift Code
For anyone who came here looking for the similar question answered in Swift:
Perhaps a cleaner (adding to Sankalp's answer), and more 'Swifty' approach:
func addSpaces(to givenString: String) -> String{
var string = givenString
//indexOffset is needed because each time replaceSubrange is called, the resulting count is incremented by one (owing to the fact that a space is added to every capitalised letter)
var indexOffset = 0
for (index, character) in string.characters.enumerated(){
let stringCharacter = String(character)
//Evaluates to true if the character is a capital letter
if stringCharacter.lowercased() != stringCharacter{
guard index != 0 else { continue } //"ILoveSwift" should not turn into " I Love Swift"
let stringIndex = string.index(string.startIndex, offsetBy: index + indexOffset)
let endStringIndex = string.index(string.startIndex, offsetBy: index + 1 + indexOffset)
let range = stringIndex..<endStringIndex
indexOffset += 1
string.replaceSubrange(range, with: " \(stringCharacter)")
}
}
return string
}
You call the function like so:
var string = "iLoveSwiftCode"
addSpaces(to: string)
//Result: string = "i Love Swift Code"
Alternatively, if you prefer extensions:
extension String{
mutating func seperatedWithSpaces(){
//indexOffset is needed because each time replaceSubrange is called, the resulting count is incremented by one (owing to the fact that a space is added to every capitalised letter)
var indexOffset = 0
for (index, character) in characters.enumerated(){
let stringCharacter = String(character)
if stringCharacter.lowercased() != stringCharacter{
guard index != 0 else { continue } //"ILoveSwift" should not turn into " I Love Swift"
let stringIndex = self.index(self.startIndex, offsetBy: index + indexOffset)
let endStringIndex = self.index(self.startIndex, offsetBy: index + 1 + indexOffset)
let range = stringIndex..<endStringIndex
indexOffset += 1
self.replaceSubrange(range, with: " \(stringCharacter)")
}
}
}
}
Call the method from a string:
var string = "iLoveSwiftCode"
string.seperatedWithSpaces()
//Result: string = "i Love Swift Code"
You could try making a new string that is a lowercase copy of the original string. Then compare the two strings and insert spaces wherever the characters are different.
Use the NSString method to turn to lowercase.
- (NSString *)lowercaseString

NSString search for substring/ csv File IO

This has given me quite a big headache. For whatever reason, when I use this code, the if statement always evaluates to false:
while(!feof(file))
{
NSString *line = [self readNSString:file];
NSLog(#"%#", line);
NSLog(#"%#", search);
NSRange textRange;
textRange =[line rangeOfString:search];
if(textRange.location != NSNotFound)
{
NSString *result = [line substringFromIndex:NSMaxRange([line rangeOfString:search])];
resultView.text = result;
}
else
{
resultView.text = #"Not found";
}
}
When the functions execute, the two NSLogs tell me that the "line" and "search" strings are what they should be, so then why does the if statement always evaluate to false? I must be missing something simple, having another set of eyes would be great. Thanks
edit: (function "readNSString")
- (NSString*)readNSString:(FILE*) file
{
char buffer[300];
NSMutableString *result = [NSMutableString stringWithCapacity:256];
int read;
do
{
if(fscanf(file, "%299[^\n]%n%*c", buffer, &read) == 1)
[result appendFormat:#"%s", buffer];
else
break;
} while(r == 299);
return result;
}
edit 2:
search is set with a call to the first function, with an NSString* variable as a parameter, like this:
NSString *textFieldText = [[NSString alloc]
initWithFormat:#"%#", textField.text];
[self readFile:textFieldText];
edit 3 (NSLogs output)
line: Germany Italy France
search: Italy
I think that you are using the rangeOfString and the NSNotFound etc. correctly, so the problem is possibly to do with the creation of the string from the data read from the file using the appendFormat:#"%s".
I suspect there may be an encoding issue between your two string formats - I would investigate whether the "%s" encodes the null terminated C string properly into the same format as a unicode NSString with the appropriate encoding.
Try hard coding the value you are getting from the readNSString function as a string literal in code just for testing and see if that comparison works, if so this would tend to indicate it probably is something to do with the encoding of the string created from the file.