Yet another NSScanner characterSetWithCharactersInString newb - objective-c

Let's assume I have a string ("G00 X0.0000 Y0.0000") and I need to to parse its contents. Here is my code:
NSCharacterSet *params = [NSCharacterSet characterSetWithCharactersInString:#"XY"];
//setup the scanner
NSScanner *scanner = [NSScanner scannerWithString:stringToBeScanned];
NSString *scanned = nil;
//scan the string
NSLog(#"%#", stringToBeScanned);
while ([scanner scanUpToCharactersFromSet:params intoString:&scanned]) {
struct keypair code;
code.key = [scanned characterAtIndex:0];
code.value = [[scanned substringFromIndex:1] doubleValue];
NSLog(#"--> %# [%lu]= (%c, %.4f)", scanned, [scanner scanLocation], code.key, code.value);
}
And the output to NSLog:
G00 X0.0000 Y0.0000
--> G00 [4]= (G, 0.0000)
My characterSet includes both 'X' and 'Y' and I can't figure out why my NSScanner won't scan in the 'X0.0000 ' - it should find that Y and pull in everything from X up to Y according to my understanding.
I can see from the scanLocation that the scanner is stopping at index 4 (correctly), but the loop either doesn't continue or evaluates to false. Shouldn't the scanner keep looping and finding my delimiters (from the characterSet) and grabbing data?

scanUpToCharactersFromSet:intoString: scans up to the "X" and gives you the characters it scanned "G00 ".
Note that it does not scan the "X". When you call the method again, it looks at the next character (the "X"), notices that it is a character in the set, and stops scanning. As it scanned no characters, it then returns NO.
To scan the "X" (or "Y"), you will want to use scanCharactersFromSet:intoString: as well.

I solved this issue. Basically I receive a string with a list of "codes" followed by a value associated with that command/parameter. There could several different "commands" in each string, or none at all. The key was to use scanCharactersFromSet: and scanUpToCharactersFromSet: in order to capture the right pairings and parse the entire string while staying very flexible. It's a little ugly, I know.
Here is my code:
//setup the scanner
NSScanner *scanner = [NSScanner scannerWithString:[self stringByAppendingString:#"!"]];
NSCharacterSet *codeset = [NSCharacterSet characterSetWithCharactersInString:#"GMTFIJKPRSXYZ!"];
NSString *scanned = nil;
char codechar;
//perform the first scan
[scanner scanCharactersFromSet:codeset intoString:&scanned];
if (scanned)
codechar = [scanned characterAtIndex:0];
//scan the string
while ([scanner scanUpToCharactersFromSet:codeset intoString:&scanned]) {
struct keypair code;
code.key = codechar;
code.value = [scanned doubleValue];
NSLog(#"--> (%c, %.4f)", code.key, code.value);
//skip over the delimeter we encountered
[scanner scanCharactersFromSet:codeset intoString:&scanned];
if (scanned)
codechar = [scanned characterAtIndex:0];
}

Related

Trim whitespace in between characters

i just updated to ios 7 sdk, and I would like to trim/replace the whitespace between characters of a string whereby the numbers are taken out from ABAddressBook.
I have tried using the replace " " with "" code below, but this code doesnt seems to work in ios7 sdk, it works fine in ios 6 sdk by the way.
NSString *TrimmedNumberField = [self.numberField.text
stringByReplacingOccurrencesOfString:#" " withString:#""];
is there any other way I could do it in IOS 7?
EDIT:
It's a phone number type that I'm trying.
Input: "+65 12 345 6789"
The output i got from NSLog is " 12 345 6789"
I realized that when I added into NSDictionary and view it in NSLog, it appears that it contains a unix code representation of \u00a0 which is similar to the "dot in the middle" which is not equals to a fullstop.
thanks in advance.
Found the answer from here
phoneNumber = [phoneNumber stringByReplacingOccurencesOfString:#"." withString:#""];
// where #"." was created by typing Option+ Spacebar
The number is extracted from ABAddressbook.
You can loop over the string and remove whitespace as long as there is any
NSString *someString = #"A string with multiple spaces and other whitespace.";
NSMutableString *mutableCopy = [someString mutableCopy];
// get first occurance of whitespace
NSRange range = [mutableCopy rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
// If there is a match for the whitespace ...
while (range.location != NSNotFound) {
// ... delete it
[mutableCopy deleteCharactersInRange:range];
// and get the next whitespace
range = [mutableCopy rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
}
// no more whitespace. You can get back to an immutable string
someString = [mutableCopy copy];
The result with the string above is Astringwithmultiplespacesandotherwhitespace.
Try This:
NSString *str = #" untrimmed string ";
NSString *trimmed = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Try This
[yourString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
whitespaceCharacterSet Apple Documentation for iOS says
Returns an NSData object encoding the receiver in binary format.
(NSData *)bitmapRepresentation
Return Value
An NSData object encoding the receiver in binary format.
Discussion
This format is suitable for saving to a file or otherwise transmitting or archiving.
A raw bitmap representation of a character set is a byte array of 2^16 bits (that is, 8192 bytes). The value of the bit at position n represents the presence in the character set of the character with decimal Unicode value n. To test for the presence of a character with decimal Unicode value n in a raw bitmap representation, use an expression such as the following:
So Try This
NSString *testString = #" Eek! There are leading and trailing spaces ";
NSString *trimmedString = [testString stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];

Parsing SRT file with Objective C

Text example:
1
00:00:00,000 --> 00:00:01,000
This is the first line
2
00:00:01,000 --> 00:00:02,000
This is the second line
3
00:00:02,000 --> 00:00:03,000
This is the last line
In JavaScript I would parse this with a regular expression certainly. I'm just wondering, is that the best way to do this in Obj C? I'm sure I could figure out a way to do this, but I'm wanting to do it an appropriate way.
I only need to know where to start and I'm happy to do the rest, but for understanding sake I'm going to end up with something like this (pseudo code):
NSDictionary
index -> [0-9]+
start -> hh:mm:ss,mmm
end -> hh:mm:ss,mmm
text -> one of the lines of text
In this case, I'd be parsing three entries into my dictionary.
Some background: I wrote a small app and created a file called stuff.srt containing your examples that resides in the bundle; hence, my means of accessing it.
This is just a quick and dirty thing, a proof-of-concept. Note that it doesn't check results. Real applications always check their results. As you can see, the work takes place in the -applicationDidFinishLaunching: method (I'm working in Mac OS X, not iOS).
EDIT:
It's been pointed out that the code as originally posted didn't handle multiple text lines correctly. To address this, I take advantage of the fact that SRT files use CRLF as their line breaks, and search for two occurrences of this sequence. I then change all occurrences of CRLF in the text string to spaces, based on what I observed here. This doesn't account for leading or trailing spaces in each line of the text.
I changed the contents of the stuff.srt file to this:
1
00:00:00,000 --> 00:00:01,000
This is the first line
and it has a secondary line
2
00:00:01,000 --> 00:00:02,000
This is the second line
3
00:00:02,000 --> 00:00:03,000
This is the last line
and it has a secondary line too
and the code has been revised as follows (I also put everything into an #autoreleasepool directive; there might be a lot of autoreleased objects generated in the course of parsing the file!):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"stuff" ofType:#"srt"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
NSScanner *scanner = [NSScanner scannerWithString:string];
while (![scanner isAtEnd])
{
#autoreleasepool
{
NSString *indexString;
(void) [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&indexString];
NSString *startString;
(void) [scanner scanUpToString:#" --> " intoString:&startString];
// My string constant doesn't begin with spaces because scanners
// skip spaces and newlines by default.
(void) [scanner scanString:#"-->" intoString:NULL];
NSString *endString;
(void) [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&endString];
NSString *textString;
// (void) [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&textString];
// BEGIN EDIT
(void) [scanner scanUpToString:#"\r\n\r\n" intoString:&textString];
textString = [textString stringByReplacingOccurrencesOfString:#"\r\n" withString:#" "];
// Addresses trailing space added if CRLF is on a line by itself at the end of the SRT file
textString = [textString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
// END EDIT
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
indexString, #"index",
startString, #"start",
endString , #"end",
textString , #"text",
nil];
NSLog(#"%#", dictionary);
}
}
}
The revised output looks like this:
2013-02-09 16:10:17.727 SRTFileScan[4846:303] {
end = "00:00:01,000";
index = 1;
start = "00:00:00,000";
text = "This is the first line and it has a secondary line";
}
2013-02-09 16:10:17.729 SRTFileScan[4846:303] {
end = "00:00:02,000";
index = 2;
start = "00:00:01,000";
text = "This is the second line";
}
2013-02-09 16:10:17.730 SRTFileScan[4846:303] {
end = "00:00:03,000";
index = 3;
start = "00:00:02,000";
text = "This is the last line and it has a secondary line too";
}
One other thing I learned from what I've read today: The SRT file format originated in France, and the comma seen in the input is the decimal separator used there.
Apple has a sample code to parse subtitle files. Check the relevant part here:
https://developer.apple.com/library/mac/samplecode/avsubtitleswriterOSX/Listings/avsubtitleswriter_SubtitlesTextReader_m.html#//apple_ref/doc/uid/DTS40013409-avsubtitleswriter_SubtitlesTextReader_m-DontLinkElementID_5
My suggest is to use a NSDateFormatter to parse the second line. I would split that string in two strings (see componentsSeparatedByString: in NSString class reference). This while reading the file line per line.
So the loop would be:
If the file contains again data, read the next line;
If the next line is a multiple of 4, allocate a new object. This object should be able to contain two dates, one integer and one string;
If the next line is not a multiple of 4, read the line and assign it's value to the corresponding field.

Creating substrings from text file

I have a text file that contains two lines of numbers, all I want to do is turn each line into a string, then add it into an array (called fields). My problem arrises when trying to find the EOF character. I can read from the file with no problem: I turn it's content into a NSString, then pass to this method.
-(void)parseString:(NSString *)inputString{
NSLog(#"[parseString] *inputString: %#", inputString);
//the end of the previous line, this is also the start of the next lien
int endOfPreviousLine = 0;
//count of how many characters we've gone through
int charCount = 0;
//while we havent gone through every character
while(charCount <= [inputString length]){
NSLog(#"[parseString] while loop count %i", charCount);
//if its an end of line character or end of file
if([inputString characterAtIndex:charCount] == '\n' || [inputString characterAtIndex:charCount] == '\0'){
//add a substring into the array
[fields addObject:[inputString substringWithRange:NSMakeRange(endOfPreviousLine, charCount)]];
NSLog(#"[parseString] string added into array: %#", [inputString substringWithRange:NSMakeRange(endOfPreviousLine, charCount)]);
//set the endOfPreviousLine to the current char count, this is where the next string will start from
endOfPreviousLine = charCount+1;
}
charCount++;
}
NSLog(#"[parseString] exited while. endOfPrevious: %i, charCount: %i", endOfPreviousLine, charCount);
}
The contents of my text file look like this:
123
456
I can get the first string (123) no problem. The call would be:
[fields addObject:[inputString substringWithRange:NSMakeRange(0, 3)]];
Next, I make the call for the second String:
[fields addObject:[inputString substringWithRange:NSMakeRange(4, 7)]];
But I get an error, and I think it is because my index is out of bounds. Since the index starts from 0, there is no index 7 (well I think its supposed to be the EOF character), and I get an error.
To sum everything up: I don't know how to deal with an index of 7 when there are only 6 characters + the EOF character.
Thanks.
You can use componentsSeparatedByCharactersInSet: to get the effect that you are looking for:
-(NSArray*)parseString:(NSString *)inputString {
return [inputString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
}
Short answer is to use [inputString componentsSeparatedByString:#"\n"] and get the array of numbers.
Example:
Use the following code to get the lines in an array
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:#"aaa" ofType:#"txt"];
NSString *str = [[NSString alloc] initWithContentsOfFile: path];
NSArray *lines = [str componentsSeparatedByString:#"\n"];
NSLog(#"str = %#", str);
NSLog(#"lines = %#", lines);
The above code assumes that you have a file called "aaa.txt" in your resources which is plain text file.

How to use NSScanner to scan from a string

I have a string which looks like:
#chat :hi there
And I'd like to scan all the text from the : to a string, so it ends like hi there
I've tried
[[NSScanner scannerWithString:argument] scanUpToString:#":" intoString:&newarg];
But newarg contains only #chat. How this can be achieved?
Example String:
#chat :Hello World,
#chat :How are you doing?
Code:
NSString *theString = #"#chat :Hello World,\n"
"#chat :How are you doing?";
NSScanner *theScanner = [NSScanner scannerWithString:theString];
NSCharacterSet *seperator = [NSCharacterSet characterSetWithCharactersInString:#":"];
NSCharacterSet *newLine = [NSCharacterSet newlineCharacterSet];
NSString *theText;
while ([theScanner isAtEnd] == NO) {
[theScanner scanUpToCharactersFromSet:seperator intoString:NULL];
[theScanner setScanLocation: [theScanner scanLocation]+1];
[theScanner scanUpToCharactersFromSet:newLine intoString:&theText];
NSLog(#"%#",theText);
}
Output:
Hello World,
How are you doing?
Consider using an if statement to iterate through the scan. You've always told the computer to scan everything until the character ":", but it sounds like you actually want to scan everything AFTER the ":" character. Anne's answer provides an excellent example of such.

Using NSScanner to parse a string

I have a string with formatting tags in it, such as There are {adults} adults, and {children} children. I have a dictionary which has "adults" and "children" as keys, and I need to look up the value and replace the macros with that value. This is fully dynamic; the keys could be anything (so I can't hardcode a stringByReplacingString).
In the past, I've done similar things before just by looping through a mutable string, and searching for the characters; removing what I've already searched for from the source string as I go. It seems like this is exactly the type of thing NSScanner is designed for, so I tried this:
NSScanner *scanner = [NSScanner scannerWithString:format];
NSString *foundString;
scanner.charactersToBeSkipped = nil;
NSMutableString *formatedResponse = [NSMutableString string];
while ([scanner scanUpToString:#"{" intoString:&foundString]) {
[formatedResponse appendString:[foundString stringByReplacingOccurrencesOfString:#"{" withString:#""]]; //Formatted string contains everything up to the {
[scanner scanUpToString:#"}" intoString:&foundString];
NSString *key = [foundString stringByReplacingOccurrencesOfString:#"}" withString:#""];
[formatedResponse appendString:[data objectForKey:key]];
}
NSRange range = [format rangeOfString:#"}" options:NSBackwardsSearch];
if (range.location != NSNotFound) {
[formatedResponse appendString:[format substringFromIndex:range.location + 1]];
}
The problem with this is that when my string starts with "{", then the scanner returns NO, instead of YES. (Which is what the documentation says should happen). So am I misusing NSScanner? The fact that scanUpToString doesn't include the string that was being searched for as part of its output seems to make it almost useless...
Can this be easily changed to do what I want, or do I need to re-write using a mutable string and searching for the characters manually?
Use isAtEnd to determine when to stop. Also, the { and } are not included in the result of scanUpToString:, so they will be at the beginning of the next string, but the append after the loop is not necessary since the scanner will return scanned content even if the search string is not found.
// Prevent scanner from ignoring whitespace between formats. For example, without this, "{a} {b}" and "{a}{b}" and "{a}
//{b}" are all equivalent
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:#""]];
while(![scanner isAtEnd]) {
if([scanner scanUpToString:#"{" intoString:&foundString]) {
[formattedResponse appendString:foundString];
}
if(![scanner isAtEnd]) {
[scanner scanString:#"{" intoString:nil];
foundString = #""; // scanUpToString doesn't modify foundString if no characters are scanned
[scanner scanUpToString:#"}" intoString:&foundString];
[formattedResponse appendString:[data objectForKey:foundString];
[scanner scanString:#"}"];
}
}