I want to manipulate NSString in obj-c here is what I want to do :
iterate a string though a for-each / for loop and shift left (<<) each character of NSString
but I don't know how should I iterate through the NSString's characters and how to use shift operator in obj-c.
I'm fairly new in objective-c .
regards
Code:
NSString * string = #"Anne";
int length = [string length];
for(int index = 0; index < length; index++) {
unichar character = [string characterAtIndex:index];
NSLog(#"%C",character);
}
Output:
A
n
n
e
NSStrings are immutable; mutableCopyWithZone: will get you an (implicitly retained) NSMutableString. However, NSMutableString doesn't have a way of setting individual characters. It would be easier to get an array of characters using one of the many methods (e.g. getCharacters:range: for wide characters, or cStringUsingEncoding:, getCString:maxLength:encoding: or UTF8String for c-style strings), then operate on that (note some methods return const strings), then construct a new string using (e.g.) initWithCString:encoding:. Keep in mind that, depending on what you're trying to accomplish, shifting bytes may not give you the result you expect, due to encoding issues and multibyte characters.
You can get the length of a string using length, which is the number of characters in the string (also the size, in unichars, of a buffer to hold UTF-16 data, not including a null-terminator), or lengthOfBytesUsingEncoding:, which will tell you the size (number of bytes) needed for a buffer to hold the contents of the string (not including a null-terminator). maximumLengthOfBytesUsingEncoding: can also be used for a buffer size, though it may be larger than the actual necessary size. For variable-length encodings, the maximum size is the largest possible character size (e.g. 3 for UTF-8 encoded unichars) times the number of characters.
Looping and shifting is otherwise the same as in C: initialize the index variable to the lower bound (0) and loop until the index variable exceeds the upper bound.
NSUInteger i;
NSString *result=nil;
unichar *data;
NSRange dataRange = {0,0};
dataRange.length = [string length];
if ((data = malloc(dataRange.length * sizeof(unichar)))) {
[string getCharacters:data range:dataRange];
for (i=0; i < dataRange.length; ++i) {
// shiftAmount is declared elsewhere
data[i] <<= shiftAmount;
}
result = [[NSString alloc] initWithCharacters:data length:dataRange.length];
} else {
// malloc() failed; handle error
...
}
If the data isn't string data but bytes, NSData/NSMutableData would be more appropriate.
Related
I'm new in objective-C and I'm not getting to converting an audio file to char exactly I need. It is ignoring a sequence of zeros (0) and it's deforming the data structure.
My code is so:
-(NSString *) dataToHex:(NSData*) data {
const unsigned char *dbytes = (unsigned char*)[data bytes];
NSMutableString *hexStr =
[NSMutableString stringWithCapacity:[data length]/**2*/];
int i;
for (i = 0; i < [data length]; i++) {
[hexStr appendFormat:#"%x", dbytes[i]];
}
return [NSString stringWithString: hexStr];
}
Thank you very much.
If I understand your problem correctly the issue is with your format %x - this produces a hexadecimal text representation with sufficient digits to represent the value and without any leading zeroes.
For example the value 32 will produce the text 20, while the value 12 produces c - only one character long.
If you wish to convert to hex representation and then back again each of your byte values needs to produce the same number of characters - as otherwise you can't know where the boundaries are between each byte's representation.
To do this you can use the format %02x, which means always produce two characters padding with zeroes as required. For example with this format 12 will produce 0c.
HTH
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 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!
I found the following code on the internet, it converts NSString representations such as
#"00F04100002712" into an actual array of bytes. The code works and does generate the correct output; I just don't understand why there is char byte_chars[3] instead of char byte_chars[2] since only the first two positions are used in the code.
void hexStringToBytes(NSString *s, NSMutableData *data)
{
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int commandLength = (int)[s length];
// convert hex values to bytes
for (int i=0; i < commandLength/2; i++)
{
byte_chars[0] = [s characterAtIndex:i*2];
byte_chars[1] = [s characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
}
I think it has something to do with the strtol function call but I am not sure what.
Can someone explain how and why this works?
C style strings have a terminating zero (aka null) character. An ASCII representation of an 8 bit byte in hexadecimal will be two characters plus that terminator.
Yes it does. strtol expects a "string". In C strings are null terminated. Thus the extra byte for the null.
C strings must be NULL (0) terminated. Since this is using a C string with a function expecting NULL terminated strings, the character array must have space for the NULL.
C strings must be NUL (0) terminated. The call to strtol function expects that.
You are messed up with C, C++ and Objective-C.
C++ and Obj-c uses full use of array. C++ creates an array of size + extra 1 space for NULL ('\0'). Obj-c has NSString class that is just a pointer, so NULL is not required.
In C you need to terminate an array of characters by NULL ('\0) and the last space is used from the allocated size. So if you write char str[10]; then you are allowed to use 9 characters and last one is automatically given to NULL.
In you code snippet you are using strtol, this is a C function which expects a C-char-array. there for last space is not used explicitly. NULL is occupying that place.
How can I enumerate NSString by pulling each unichar out of it? I can use characterAtIndex but that is slower than doing it by an incrementing unichar*. I didn't see anything in Apple's documentation that didn't require copying the string into a second buffer.
Something like this would be ideal:
for (unichar c in string) { ... }
or
unichar* ptr = (unichar*)string;
You can speed up -characterAtIndex: by converting it to it's IMP form first:
NSString *str = #"This is a test";
NSUInteger len = [str length]; // only calling [str length] once speeds up the process as well
SEL sel = #selector(characterAtIndex:);
// using typeof to save my fingers from typing more
unichar (*charAtIdx)(id, SEL, NSUInteger) = (typeof(charAtIdx)) [str methodForSelector:sel];
for (int i = 0; i < len; i++) {
unichar c = charAtIdx(str, sel, i);
// do something with C
NSLog(#"%C", c);
}
EDIT: It appears that the CFString Reference contains the following method:
const UniChar *CFStringGetCharactersPtr(CFStringRef theString);
This means you can do the following:
const unichar *chars = CFStringGetCharactersPtr((__bridge CFStringRef) theString);
while (*chars)
{
// do something with *chars
chars++;
}
If you don't want to allocate memory for coping the buffer, this is the way to go.
Your only option is to copy the characters into a new buffer. This is because the NSString class does not guarantee that there is an internal buffer you can use. The best way to do this is to use the getCharacters:range: method.
NSUInteger i, length = [string length];
unichar *buffer = malloc(sizeof(unichar) * length);
NSRange range = {0,length};
[string getCharacters:buffer range:range];
for(i = 0; i < length; ++i) {
unichar c = buffer[i];
}
If you are using potentially very long strings, it would be better to allocate a fixed size buffer and enumerate the string in chunks (this is actually how fast enumeration works).
I created a block-style enumeration method that uses getCharacters:range: with a fixed-size buffer, as per ughoavgfhw's suggestion in his answer. It avoids the situation where CFStringGetCharactersPtr returns null and it doesn't have to malloc a large buffer. You can drop it into an NSString category, or modify it to take a string as a parameter if you like.
-(void)enumerateCharactersWithBlock:(void (^)(unichar, NSUInteger, BOOL *))block
{
const NSInteger bufferSize = 16;
const NSInteger length = [self length];
unichar buffer[bufferSize];
NSInteger bufferLoops = (length - 1) / bufferSize + 1;
BOOL stop = NO;
for (int i = 0; i < bufferLoops; i++) {
NSInteger bufferOffset = i * bufferSize;
NSInteger charsInBuffer = MIN(length - bufferOffset, bufferSize);
[self getCharacters:buffer range:NSMakeRange(bufferOffset, charsInBuffer)];
for (int j = 0; j < charsInBuffer; j++) {
block(buffer[j], j + bufferOffset, &stop);
if (stop) {
return;
}
}
}
}
The fastest reliable way to enumerate characters in an NSString I know of is to use this relatively little-known Core Foundation gem hidden in plain sight (CFString.h).
NSString *string = <#initialize your string#>
NSUInteger stringLength = string.length;
CFStringInlineBuffer buf;
CFStringInitInlineBuffer((__bridge CFStringRef) string, &buf, (CFRange) { 0, stringLength });
for (NSUInteger charIndex = 0; charIndex < stringLength; charIndex++) {
unichar c = CFStringGetCharacterFromInlineBuffer(&buf, charIndex);
}
If you look at the source code of these inline functions, CFStringInitInlineBuffer() and CFStringGetCharacterFromInlineBuffer(), you'll see that they handle all the nasty details like CFStringGetCharactersPtr() returning NULL, CFStringGetCStringPtr() returning NULL, defaulting to slower CFStringGetCharacters() and caching the characters in a C array for fastest access possible. This API really deserves more publicity.
The caveat is that if you initialize the CFStringInlineBuffer at a non-zero offset, you should pass a relative character index to CFStringInlineBuffer(), as stated in the header comments:
The next two functions allow fast access to the contents of a string, assuming you are doing sequential or localized accesses. To use, call CFStringInitInlineBuffer() with a CFStringInlineBuffer (on the stack, say), and a range in the string to look at. Then call CFStringGetCharacterFromInlineBuffer() as many times as you want, with a index into that range (relative to the start of that range). These are INLINE functions and will end up calling CFString only once in a while, to fill a buffer. CFStringGetCharacterFromInlineBuffer() returns 0 if a location outside the original range is specified.
I don't think you can do this. NSString is an abstract interface to a multitude of classes that make no guarantees about the internal storage of the character data, so it's entirely possible there is no character array to get a pointer to.
If neither of the options mentioned in your question are suitable for your app, I'd recommend either creating your own string class for this purpose, or using raw malloc'ed unichar arrays instead of string objects.
This will work:
char *s = [string UTF8String];
for (char *t = s; *t; t++)
/* use as */ *t;
[Edit] And if you really need unicode characters then you have no option but to use length and characterAtIndex. From the documentation:
The NSString class has two primitive methods—length and characterAtIndex:—that provide the basis for all other methods in its interface. The length method returns the total number of Unicode characters in the string. characterAtIndex: gives access to each character in the string by index, with index values starting at 0.
So your code would be:
for (int index = 0; index < string.length; index++)
{
unichar c = [string characterAtIndex: index];
/* ... */
}
[edit 2]
Also, don't forget that NSString is 'toll-free bridged' to CFString and thus all the non-Objective-C, straight C-code interface functions are usable. The relevant one would be CFStringGetCharacterAtIndex