Search code examples
objective-cobjectnsscanner

Using NSScanner to scan multiple lined text file


I have experience with Java and Android development, and am now attempting to learn Objective-C and iPhone/iPad development. In order to help teach myself, I am re-writing an application I made for android over to iPhone.

The specific problem I am having has to do with using the NSScanner class. In my android application, I read in a txt file(with multiple lines) and built "member" objects that get loaded into an array. Each member has general contact info being a first name, last name, phone number, email, pledge class and major. This text file was written by me so I know the exact formatting. Below is an example of one line in the text file.

Fawzy   Jake    8144425471  [email protected] Beta    Criminal Justice & Psych Minor

In Java/android I was able to load the desired results by using multiple scan.Next() and then a scan.nextLine() for the major. Can anyone help point me in the right direction of how to use NSScanner/objective-c to perform a similar operation? Specifically, I would like to know how to properly scan through the text file/string, then build my member object with the information I grabbed from the string.

Below is an example of my code that I have been working on, but am having trouble putting what I want to do logically into syntax as I am not familiar with objective-c.

- (NSString *)loadFileToString{
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"akpsi_contact_list"
                                                     ofType:@"txt"];
NSError *error = nil;
NSString *fileContent = [NSString stringWithContentsOfFile:filePath
                                                  encoding:NSUTF8StringEncoding
                                                     error:&error];
if(error)
{
    NSLog(@"ERROR while loading from file: %@", error);
}
return fileContent;
}

-(void)readFileString{
NSScanner *scanner = [NSScanner scannerWithString: self.loadFileToString];
while ([scanner isAtEnd] == NO) {

    //member object
    AKPsiMember *member;

    //temporary variables
    NSString *thisFirstName;
    NSString *thisLastName;
    NSString *thisPhoneNum;
    NSString *thisEmail;
    NSString *thisPledge;
    NSString *thisMajor;

    //scan one line, save 
    [scanner scanUpToString:@" " intoString:&thisFirstName];
    [scanner scanUpToString:@" " intoString:&thisLastName];
    [scanner scanUpToString:@" " intoString:&thisPhoneNum];
    [scanner scanUpToString:@" " intoString:&thisEmail];
    [scanner scanUpToString:@" " intoString:&thisPledge];
    [scanner scanUpToString:@" " intoString:&thisMajor];
    //build member object with temporary variables
    // implementation must continue to next line...
}    

}

Also my member object/class

.h

@interface AKPsiMember : NSObject{
NSString *firstName;
NSString *lastName;
NSString *emailAddress;
NSString *pledgeClass;
NSString *major;
NSString *phoneNum;


}

@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSString *emailAddress;
@property (nonatomic, strong) NSString *pledgeClass;
@property (nonatomic, strong) NSString *major;
@property (nonatomic, strong) NSString *phoneNum;


@end

And my .m

@implementation AKPsiMember

@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@synthesize phoneNum = _phoneNum;
@synthesize emailAddress = _emailAddress;
@synthesize pledgeClass = _pledgeClass;
@synthesize major = _major;

- (NSString *)phoneNum:(NSNumber *)num
{
if(num == 0)
{
    NSString *temp1 = [self.phoneNum substringWithRange:NSMakeRange(0, 3)];
    NSString *temp2 = [self.phoneNum substringWithRange:NSMakeRange(3, 6)];
    NSString *temp3 = [self.phoneNum substringWithRange:NSMakeRange(6, 9)];

    NSString *formatNum = [NSString stringWithFormat: @"(%@) %@-%@", temp1, temp2, temp3];
    return formatNum;
}
else{
    return self.phoneNum;
}
}

@end

Solution

  • After you scanned all your fields you should scan the newline character in order to proceed to the next line:

    NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
    [scanner scanUpToCharactersFromSet:whitespace intoString:&thisFirstName];
    [scanner scanCharactersFromSet:whitespace intoString:nil];        
    [scanner scanUpToCharactersFromSet:whitespace intoString:&thisLastName];
    [scanner scanCharactersFromSet:whitespace intoString:nil];
    [scanner scanUpToCharactersFromSet:whitespace intoString:&thisPhoneNum];
    [scanner scanCharactersFromSet:whitespace intoString:nil];
    [scanner scanUpToCharactersFromSet:whitespace intoString:&thisEmail];
    [scanner scanCharactersFromSet:whitespace intoString:nil];
    [scanner scanUpToCharactersFromSet:whitespace intoString:&thisPledge];
    [scanner scanCharactersFromSet:whitespace intoString:nil];
    
    NSCharacterSet *newLineCharacterSet = [NSCharacterSet newlineCharacterSet];
    [scanner scanUpToCharactersFromSet:newLineCharacterSet intoString:&thisMajor];
    [scanner scanCharactersFromSet:newLineCharacterSet intoString:nil];
    

    Create the NSMutableArray before the loop.

    NSMutableArray *members = [NSMutableArray array];
    

    Set the values inside the loop and add your Object to the members array.

    AKPsiMember *member = [[AKPsiMember alloc] init];
    member.firstName = thisFirstName;
    // ... set other properties
    [members addObject:member];
    

    This code will break when there are whitespaces in between the first fields of your dataset. Obviously this could be broken down into methods and results should be checked.