Replacing character within cstring - getting bad access - objective-c

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.

Related

Concatenating an int to a string in Objective-c

How do I concatenate the int length to the string I'm trying to slap into that array so it is "C10" given length == 10, of course. I see #"%d", intVarName way of doing it used else where. In Java I would of done "C" + length;. I am using the replaceObjectAtIndex method to replace the empty string, "", that I have previously populated the MSMutableArray "board" with. I am getting an error though when I add the #"C%d", length part at the end of that method (second to last line, above i++).
As part of my homework I have to randomly place "Chutes" (represented by a string of format, "C'length_of_chute'", in this first assignment they will always be of length 10 so it will simply be "C10") onto a game board represented by an array.
-(void)makeChutes: (int) length {// ??Change input to Negative number, Nvm.
//??Make argument number of Chutes ??randomly?? across the board.
for(int i = 0; i < length;){
int random = arc4random_uniform(101);
if ([[board objectAtIndex:random] isEqual:#""]) {
//[board insertObject:#"C%d",length atIndex:random];
[board replaceObjectAtIndex:random withObject:#"C%d",length];
i++;
}
}
}
Please ignore the extra and junk code in there, I left it in for context.
In Objective-C the stringWithFormat method is used for formatting strings:
NSString *formattedString = [NSString stringWithFormat:#"C%d", length];
[someArray insertObject:formattedString];
It's often easier to create your formatted string on a line of its own in Objective-C, since as you can see the call can be fairly verbose!

Parsing arithmetic expression for long numbers that need formatting

I am trying to make a simple calculator app. Currently, the app works perfectly. One problem: It's smart enough to change results into formatted numbers (800000 = 800,000), but not full expressions (200*600/21000 = 200*600/21,000).
I would like to be able to have a method that I could feed a string and get back a string of properly formatted numbers with operations still inside the string.
Example:
I feed the method 30000/80^2. Method gives back 30,000/80^2.
EDIT: People seem to be misunderstanding the question (Or it's possible I am misunderstanding the answers!) I want to be able to separate the numbers - 60000/200000 would separate into 60000 & 200000. I can do it from there.
Well, what's the problem? You obviously can parse the whole expression (you say calculator works), you can format single numbers (you say you can format results).
The only thing you need is to parse the expression, format all the numbers and recompose the expression...
EDIT: There is a simpler solution. For formatting, you don't need to parse the expression into a tree. You just have to find the numbers.
I suggest to create character set of all operators
NSCharacterSet* operators = [NSCharacterSet characterSetWithCharactersInString:#"+*-/^()"];
NSCharacterSet* whitespaces = [NSCharacterSet whitespaceCharacterSet];
Then split the expression using this set:
NSString* expression = [...];
NSMutableString* formattedExpression = [NSMutableString string];
NSRange numberRange = NSMakeRange(0, 0);
for (NSUInteger i = 0; i < expression.length; i++) {
unichar character = [expression characterAtIndex:i];
if ([whitespaces characterIsMember:character] || [operators characterIsMember:character]) {
if (numberRange.length > 0) {
NSString* number = [expression substringWithRange:numberRange];
NSString* formattedNumber = [self formatNumber:number];
[formattedExpression appendString:number];
numberRange.length = 0;
}
}
else if (numberRange.length == 0) {
numberRange.location = i;
numberRange.length = 1;
}
else {
numberRange.length++;
}
if ([operators characterIsMember:character]) {
[formattedExpression appendFormat:#"%C", character];
}
}
if (numberRange.length > 0) {
NSString* number = [expression substringWithRange:numberRange];
NSString* formattedNumber = [self formatNumber:number];
[formattedExpression appendString:number];
}
Note that this should work even for numbers prefixed by a sign. I am ignoring all whitespaces because if you want to have a pretty expression, you probably want to handle whitespaces differently (e.g. no space after (, space before +/-, space after - only if it's not a number sign...). In general, for handling spaces, parsing the expression into a tree would simplify matters. Also note that infix expressions are not unambiguous - that means that you should sometimes add parenthesis. However, that can't be done without parsing into a tree.
Look up NSNumberFormatter. Not only will that handle formatting of numbers, it will do so based on the user's locale.

Cipher A Message, And Then Decode It in Objective-C

I can't think of a better way to describe it, so here it is.
I'm working on a program, very simply, that you input a message you want encoded, it takes it and makes it into a coded message, such as taking 'easy' and, for example, translating it into 'gcua' (achieved by moving each letter forward two in the alphabet)
Then, the person on the receiving end could stick it into the program and it would bring it back to the original text. I'm obviously thinking something a little more complicated than moving the letters up two or something, but, I digress.
I have some really.. REALLY basic code for this. The problem I'm having is breaking each letter of the message up to apply the encoding, then putting it back together.
I'm not asking for the program, I'm more so asking of how to get up to the point of being able to encode the message. (ex. putting the string into a format possible to encode). It isn't intended to be anything elaborate, just very simple, straight to the point.
Here's a basic outline (Thanks to those who helped troubleshoot):
int main (int argc, const char * argv[]) {
#autoreleasepool {
char choice, yes = 'y', no = 'n', buf[1024];
NSString *plainText, *encrypted, *decoded;
NSLog(#"Encode (Y) or Decode (N)");
scanf("%c", &choice);
choice = tolower(choice);
if (choice == yes) {
scanf("%1024s", buf);
plainText = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];
} else if (choice == no) {
}
}
return 0;
}
%c implies a char* argument, but you are passing char**.
You should make your variables char, not a char*:
char choice, yes = 'y', no = 'n';
It would also make sense to convert choice to lower case before comparing to y and n, because you ask end-users to enter uppercase Y or N
scanf("%c", &choice);
choice = tolower(choice);
EDIT in response to the edit of the question: You cannot use %s to read a value into NSString. You should use char buf[MAX], and limit the length in the scanf:
char buf[1024];
scanf("%1024s", buf);
plainText = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];
The %s specification to scanf tells it to read into an array of char that you have already allocated. But you are passing a nil pointer to NSString instead.
Try something like this:
NSData *data = [[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile];
plainText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

How do I split an NSString by each character in the string?

I have the following code, which works as I expect. What I would like to know if there is an accepted Cocoa way of splitting a string into an array with each character of the string as an object in an array?
- (NSString *)doStuffWithString:(NSString *)string {
NSMutableArray *stringBuffer = [NSMutableArray arrayWithCapacity:[string length]];
for (int i = 0; i < [string length]; i++) {
[stringBuffer addObject:[NSString stringWithFormat:#"%C", [string characterAtIndex:i]]];
}
// doing stuff with the array
return [stringBuffer componentsJoinedByString:#""];
}
As a string is already an array of characters, that seems, ... redundant.
If you really need an NSArray of NSStrings of one character each, I think your way of creating it is OK.
But it appears questionable that your purpose cannot be done in a more readable, safe (and performance-optimized) way. One thing especially seem dangerous to me: Splitting strings into unicode characters is (most of the time) not doing what you might expect. There are characters that are composed of more than one unicode code point. and there are unicode code points that really are more than one character. Unless you know about these (or can guarantee that your input does not contain arbitrary strings) you shouldn’t poke around in unicode strings on the character level.

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.