Objective-C NSString for loop with characterAtIndex - objective-c

I'm trying to loop through a NSString, character by character, but I'm getting a EXC_BAD_ACCESS error. Do you have an idea how to do this right? I've been googling for hours now but can't figure it out.
Here is my code (.m):
self.textLength = [self.text length];
for (int position=0; position < self.textLength; position++) {
NSLog(#"%#", [self.text characterAtIndex:position]);
if ([[self.text characterAtIndex:position] isEqualToString:#"."]){
NSLog(#"it's a .");
}
}
Thanks a lot!

Characters are not object. characterAtIndex returns unichar, which is actually an integer type unsigned short. You need to use %C instead of %# in NSLog. Also character is not a NSString, so you can't send it isEqualToString. You need to use ch == '.' to compare ch against '.'.
unichar ch = [self.text characterAtIndex:position];
NSLog(#"%C", ch);
if (ch == '.') {} // single quotes around dot, not double quotes
Note that, 'a' is character, "a" is C string and #"a" is NSString. They all are different types.
When you are using %# with unichar ch in NSLog, it is trying to print an object from memory location ch which is invalid. Thus you are getting a EXC_BAD_ACCESS.

characterAtIndex: returns a unichar, so you should use NSLog(#"%C", ...) instead of #"%#".
You also cannot use isEqualToString for a unichar, just use == '.' is fine.
If you want to find the position of all '.'s, you can use rangeOfString. Refer to:
String Programming Guide: Searching, Comparing, and Sorting Strings
Position of a character in a NSString or NSMutableString

characterAtIndex: returns a unichar, which is declared as typedef unsigned short unichar; The format specifier you are using in your calls to NSLog are incorrect, you could just do NSLog(#"%u",[self.text characterAtIndex:position]); or NSLog(#"%C",[self.text characterAtIndex:position]); if you want the actual character to print out.
Also, as a result of unichar being defined the way that it is, it's not a string, so you cannot compare it to other strings. Try something like:
unichar textCharacter = '.';
if ([self.text characterAtPosition:position] == testCharacter) {
// do stuff
}

If you want to find the location of a character in a string you can use this:
NSUInteger position = [text rangeOfString:#"."].location;
if the character or text is not found you will get a NSNotFound:
if(position==NSNotFound)
NSLog(#"text not found!");

Related

Replacing character within cstring - getting bad access

Is it possible to replace a character from a c string after converting it from NSString via the UTF8string method?
For example take the code below. It is to format a string with particular rule.
- (NSString *)formatString:(NSString *)input {
if (input.length==0) {
return #"";
}
//code to determine rule
....
....
// substitute output format with input characters
if (rule) {
input = [input substringFromIndex:prefix.length];
char *string = (char *)[rule UTF8String];
int repCount = 0;
for (int i=0; i<rule.length; i++) {
if (string[i] == '#') {
if (repCount < input.length)
string[i] = [input characterAtIndex:repCount++];//bad access
else
string[i] = ' ';
}
}
NSMutableString *output = [NSMutableString stringWithCString:string encoding:NSUTF8StringEncoding];
...
... //do something with the output
return output;
} else {
return input;
}
}
Initially string[0] has '#' and it should get replaced with the character in the input. This is not happening.
In a word, NO. That buffer doesn't belong to you so leave it alone.
A couple of issues:
You are casting UTF8String, which returns a const char *, to char *. UTF8String is, by definition, returning a read-only string and you should use it as such. (You really should use casts sparingly, if at all. Certainly never use casts to override const qualifiers for variables.)
If you want to perform this C-string manipulation, you have to copy the string to your own buffer. For example, use getCString or getCharacters methods (but only after you've created a buffer to receive them, and remember to add a character for the NULL terminator).
By the way, you're also returning characterAtIndex, which is a unichar (which can be larger than 8-bits), and using it in your char * buffer (8-bits per character). I'd be wary about mixing and matching those without being very careful. It is best to pick one and stick with it (and unichar offers a little more tolerance for those non-8-bit characters).
Perhaps you check for this earlier, but you're setting string to be those characters after the prefix, and then proceed to check the next rule.length number of characters. But, as far as I can tell, you have no assurances that string actually has that many characters left in it. You should test for that, or else that will also cause problems.
Personally, I'd retire this whole C-string algorithm and employ the appropriate NSString and/or NSMutableString methods to do whatever replacement you wanted, e.g. stringByReplacingCharactersInRange, stringByReplacingOccurrencesOfString, or the equivalent NSMutableString methods, replaceCharactersInRange or replaceOccurrencesOfString.

How to get a single NSString character from an NSString

I want to get a character from somewhere inside an NSString. I want the result to be an NSString.
This is the code I use to get a single character at index it:
[[s substringToIndex:i] substringToIndex:1]
Is there a better way to do it?
This will also retrieve a character at index i as an NSString, and you're only using an NSRange struct rather than an extra NSString.
NSString * newString = [s substringWithRange:NSMakeRange(i, 1)];
If you just want to get one character from an a NSString, you can try this.
- (unichar)characterAtIndex:(NSUInteger)index;
Used like so:
NSString *originalString = #"hello";
int index = 2;
NSString *theCharacter = [NSString stringWithFormat:#"%c", [originalString characterAtIndex:index-1]];
//returns "e".
Your suggestion only works for simple characters like ASCII. NSStrings store unicode and if your character is several unichars long then you could end up with gibberish. Use
- (NSRange)rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)index;
if you want to determine how many unichars your character is. I use this to step through my strings to determine where the character borders occur.
Being fully unicode able is a bit of work but depends on what languages you use. I see a lot of asian text so most characters spill over from one space and so it's work that I need to do.
NSMutableString *myString=[NSMutableString stringWithFormat:#"Malayalam"];
NSMutableString *revString=#"";
for (int i=0; i<myString.length; i++) {
revString=[NSMutableString stringWithFormat:#"%c%#",[myString characterAtIndex:i],revString];
}
NSLog(#"%#",revString);

Objective-C: Initializing char with char at index of string

unichar myChar = [myString characterAtIndex:0];
[myNSMutableArray addObject:myChar];
I am trying to insert the first char of a string into an array, to create an array of chars. the first line does not give me an error. The second line however, provides the following error: warning: passing argument 1 of 'addObject:' makes pointer from integer without a cast
This also crashes the application with a "bad address" error. I thought this error was due to a problem with memory allocation. Can someone shed some light on this.
You can only add objects to an array. unichar is a primitive data type. You have to wrap it in an NSNumber. A unichar is an unsigned short, so you can use:
[myNSMutableArray addObject:[NSNumber numberWithUnsignedShort:[myString characterAtIndex:0]]];
One option would be to add the character to your array as a string:
unichar myChar = [myString characterAtIndex:0];
NSString * charString = [NSString stringWithFormat:#"%c", myChar];
[myNSMutableArray addObject:charString];
Note that this is probably overkill.

Empty character in Objective-C

I'm creating some code that will find a space between characters, and use the characters before the space and the ones after it. These characters are stored in a NSString. Here is what I have so far, however, it's not seeing the empty character.
NSString *tempTitle = self.title;
unsigned int indexOfSpace; // Holds the index of the character with the space
unsigned int titleLength = (unsigned int)self.title.length; // Holds the length of the title
for (unsigned int count = 0; count < titleLength; count++)
{
if ([tempTitle characterAtIndex:count] == "") // If the character at the index is blank, store this and stop
{
indexOfSpace == count;
}
else // Else, we keep on rollin'
{
NSLog(#"We're on character: %c", [tempTitle characterAtIndex:count]);
}
}
I've tried nil , empty string ("") and " " but no avail. Any ideas?
Your space character should be in single quotes, not double quotes. Single quotes get you the char type in C. (Double quotes create a string literal, which essentially functions as a char * and will never pass your comparison.)
-[NSString characterAtIndex:] returns a type unichar, which is an unsigned short, so you should be able to compare this directly to a space character ' ', if that's what you want to do.
Note that nil and empty string, are not useful here-- neither are actually characters, and in any case your string will never "contain" these.
You should see also the NSString methods for finding characters in strings directly, e.g. -[NSString rangeOfString:] and its cousins. That prevents you from writing the loop yourself, although those are unfortunately a little syntactically verbose.

Objective C unicode character comparisons

How are unicode comparisons coded? I need to test exactly as below, checking for specific letters in a string. The code below chokes: warning: comparison between pointer and integer
for (charIndex = 0; charIndex < [myString length]; charIndex++)
{
unichar testChar = [myString characterAtIndex:charIndex];
if (testChar == "A")
// do something
if (testChar == "B")
// do something
if (testChar == "C")
// do something
}
For char literals, use single quotes:
if (testChar == 'A') NSLog(#"It's an A");
Or represent the character using the code point number:
if (testChar == 0x1e01) NSLog(#"It's an A with a ring below");
The compiler sees double-quotes as a string, so builds "A" as equivalent to a const char * (which gives you there error message about the pointer).
What are you really trying to do? Doing direct character comparisons is unusual. Typically -compare: or -isEqual: would be used to compare two strings. Or NSScanner would be used to analyze the components of a string.