开发者

Reverse NSString text

开发者 https://www.devze.com 2023-03-20 23:45 出处:网络
I have been googling so much on how to do this, but how would I reverse a NSString? Ex:hi would become: ih

I have been googling so much on how to do this, but how would I reverse a NSString? Ex:hi would become: ih

I am looking for the easiest way to do this.

Thanks!

@Vince I made this method:

- (IBAction)doneKeyboard {

// first retrieve the text of textField1
NSString *myString = field1.text;
NSMutableString *reversedString = [NSMutableString string];
NSUInteger charIndex = 0;
while(myString &&开发者_Go百科amp; charIndex < [myString length]) {
    NSRange subStrRange = NSMakeRange(charIndex, 1);
    [reversedString appendString:[myString substringWithRange:subStrRange]];
    charIndex++;
}
// reversedString is reversed, or empty if myString was nil
field2.text = reversedString;
}

I hooked up that method to textfield1's didendonexit. When I click the done button, it doesn't reverse the text, the UILabel just shows the UITextField's text that I entered. What is wrong?


Block version.

NSString *myString = @"abcdefghijklmnopqrstuvwxyz";
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[myString length]];

[myString enumerateSubstringsInRange:NSMakeRange(0,[myString length]) 
                             options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
                          usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                            [reversedString appendString:substring];
                        }];

// reversedString is now zyxwvutsrqponmlkjihgfedcba


Write a simple loop to do that:

// myString is "hi"
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (charIndex > 0) {
    charIndex--;
    NSRange subStrRange = NSMakeRange(charIndex, 1);
    [reversedString appendString:[myString substringWithRange:subStrRange]];
}
NSLog(@"%@", reversedString); // outputs "ih"

In your case:

// first retrieve the text of textField1
NSString *myString = textField1.text;
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (myString && charIndex > 0) {
    charIndex--;
    NSRange subStrRange = NSMakeRange(charIndex, 1);
    [reversedString appendString:[myString substringWithRange:subStrRange]];
}
// reversedString is reversed, or empty if myString was nil
textField2.text = reversedString;


jano’s answer is correct. Unfortunately, it creates a lot of unnecessary temporary objects. Here is a much faster (more complicated) implementation that basically does the same thing, but uses memcpy and unichar buffers to keep memory allocations to a minimum.

- (NSString *)reversedString
{
    NSUInteger length = [self length];
    if (length < 2) {
        return self;
    }

    unichar *characters = calloc(length, sizeof(unichar));
    unichar *reversedCharacters = calloc(length, sizeof(unichar));
    if (!characters || !reversedCharacters) {
        free(characters);
        free(reversedCharacters);
        return nil;
    }

    [self getCharacters:characters range:NSMakeRange(0, length)];

    NSUInteger i = length - 1;
    NSUInteger copiedCharacterCount = 0;

    // Starting from the end of self, copy each composed character sequence into reversedCharacters
    while (copiedCharacterCount < length) {
        NSRange characterRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
        memcpy(reversedCharacters + copiedCharacterCount, characters + characterRange.location, characterRange.length * sizeof(unichar));
        i = characterRange.location - 1;
        copiedCharacterCount += characterRange.length;
    }

    free(characters);

    NSString *reversedString = [[NSString alloc] initWithCharactersNoCopy:reversedCharacters length:length freeWhenDone:YES];
    if (!reversedString) {
        free(reversedCharacters);
    }

    return reversedString;
}

I tested this on 100,000 random multi-byte Unicode strings with lengths between 1 and 128. This version is about 4–5x faster than jano’s.

Enumerate substrings: 2.890528
MemCopy: 0.671090
Enumerate substrings: 2.840411
MemCopy: 0.662882

Test code is at https://gist.github.com/prachigauriar/9739805.

Update: I tried this again by simply converting to a UTF-32 buffer and reversing that.

- (NSString *)qlc_reversedStringWithUTF32Buffer
{
    NSUInteger length = [self length];
    if (length < 2) {
        return self;
    }

    NSStringEncoding encoding = NSHostByteOrder() == NS_BigEndian ? NSUTF32BigEndianStringEncoding : NSUTF32LittleEndianStringEncoding;
    NSUInteger utf32ByteCount = [self lengthOfBytesUsingEncoding:encoding];
    uint32_t *characters = malloc(utf32ByteCount);
    if (!characters) {
        return nil;
    }

    [self getBytes:characters maxLength:utf32ByteCount usedLength:NULL encoding:encoding options:0 range:NSMakeRange(0, length) remainingRange:NULL];

    NSUInteger utf32Length = utf32ByteCount / sizeof(uint32_t);
    NSUInteger halfwayPoint = utf32Length / 2;
    for (NSUInteger i = 0; i < halfwayPoint; ++i) {
        uint32_t character = characters[utf32Length - i - 1];
        characters[utf32Length - i - 1] = characters[i];
        characters[i] = character;
    }

    return [[NSString alloc] initWithBytesNoCopy:characters length:utf32ByteCount encoding:encoding freeWhenDone:YES];
}

This is about 3–4x times faster than the memcpy version. The aforementioned gist has been updated with the latest version of the code.

Enumerate substrings: 2.168705
MemCopy: 0.488320
UTF-32: 0.150822
Enumerate substrings: 2.169655
MemCopy: 0.481786
UTF-32: 0.147534
Enumerate substrings: 2.248812
MemCopy: 0.505995
UTF-32: 0.154531


I thought I'd throw another version out there in case anyone's interested.. personally, I like the cleaner approach using NSMutableString but if performance is the highest priority this one is faster:

- (NSString *)reverseString:(NSString *)input {
    NSUInteger len = [input length];
    unichar *buffer = malloc(len * sizeof(unichar));
    if (buffer == nil) return nil; // error!
    [input getCharacters:buffer];

    // reverse string; only need to loop through first half
    for (NSUInteger stPos=0, endPos=len-1; stPos < len/2; stPos++, endPos--) {
        unichar temp = buffer[stPos];
        buffer[stPos] = buffer[endPos];
        buffer[endPos] = temp;
    }

    return [[NSString alloc] initWithCharactersNoCopy:buffer length:len freeWhenDone:YES];
}

I also wrote a quick test as well to compare this with the more traditional NSMutableString method (which I also included below):

// test reversing a really large string
NSMutableString *string = [NSMutableString new];
for (int i = 0; i < 10000000; i++) {
    int digit = i % 10;
    [string appendFormat:@"%d", digit];
}
NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
NSString *reverse = [self reverseString:string];
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSince1970] - startTime;
NSLog(@"reversed in %f secs", elapsedTime);

Results were:

  • using NSMutableString method (below) - "reversed in 3.720631 secs"

  • using unichar *buffer method (above) - "reversed in 0.032604 secs"

Just for reference, here's the NSMutableString method used for this comparison:

- (NSString *)reverseString:(NSString *)input {
    NSUInteger len = [input length];
    NSMutableString *result = [[NSMutableString alloc] initWithCapacity:len];
    for (int i = len - 1; i >= 0; i--) {
        [result appendFormat:@"%c", [input characterAtIndex:i]];
    }
    return result;
}


Use method with any objects: NSString,NSNumber,etc..:

NSLog(@"%@",[self reverseObject:@12345]);
NSLog(@"%@",[self reverseObject:@"Hello World"]);

Method:

-(NSString*)reverseObject:(id)string{

string = [NSString stringWithFormat:@"%@",string];
NSMutableString *endString = [NSMutableString new];

while ([string length]!=[endString length]) {
    NSRange range = NSMakeRange([string length]-[endString length]-1, 1);
    [endString appendString: [string substringWithRange:range]];
}
return endString;}

Log:

2014-04-16 11:20:25.312 TEST[23733:60b] 54321
2014-04-16 11:20:25.313 TEST[23733:60b] dlroW olleH


Swift 2.0:

1) let str = "Hello, world!" let reversed = String(str.characters.reverse()) print(reversed)

In Short:

String("This is a test string.".characters.reverse())

2)

let string = "This is a test string."
let characters = string.characters
let reversedCharacters = characters.reverse()
let reversedString = String(reversedCharacters)

The short way :

String("This is a test string.".characters.reverse())

OR

let string = "This is a test string."
let array = Array(string)
let reversedArray = array.reverse()
let reversedString = String(reversedArray)
The short way :

String(Array("This is a test string.").reverse())

Tested on Play Ground:

import Cocoa

//Assigning a value to a String variable
var str = "Hello, playground"

//Create empty character Array.
var strArray:Character[] = Character[]()

//Loop through each character in the String
for character in str {
//Insert the character in the Array variable.
strArray.append(character)
}

//Create a empty string
var reversedStr:String = ""

//Read the array from backwards to get the characters
for var index = strArray.count - 1; index >= 0;--index {
//Concatenate character to String.
reversedStr += strArray[index]
}

The shorter version:

var str = “Hello, playground”
var reverseStr = “”
for character in str {
reverseStr = character + reverseStr
}


Would it be faster if you only iterated over half the string swapping the characters at each end? So for a 5 character string, you swap characters 1 + 5, then 2 + 4 and 3 doesn't need swapped with anything.

NSMutableString *reversed = [original mutableCopyWithZone:NULL];
NSUInteger i, length;

length = [reversed length];

for (i = 0; i < length / 2; i++) {
    // Store the first character as we're going to replace with the character at the end
    // in the example, it would store 'h' 
    unichar startChar = [reversed characterAtIndex:i];

    // Only make the end range once
    NSRange endRange = NSMakeRange(length - i, 1);

    // Replace the first character ('h') with the last character ('i')
    // so reversed now contains "ii"
    [reversed replaceCharactersInRange:NSMakeRange(i, 1) 
                            withString:[reversed subStringWithRange:endRange];

    // Replace the last character ('i') with the stored first character ('h)
    // so reversed now contains "ih"
    [reversed replaceCharactersInRange:endRange
                            withString:[NSString stringWithFormat:@"%c", startChar]];
}

edit ----

Having done some tests, the answer is No, its about 6 times slower than the version that loops over everything. The thing that slows us down is creating the temporary NSStrings for the replaceCharactersInRange:withString method. Here is a method that creates only one NSString by manipulating the character data directly and seems a lot faster in simple tests.

NSUInteger length = [string length];
unichar *data = malloc(sizeof (unichar) * length);
int i;

for (i = 0; i < length / 2; i++) {
    unichar startChar = [string characterAtIndex:i];
    unichar endChar = [string  characterAtIndex:(length - 1) - i];

    data[i] = endChar;
    data[(length - 1) - i] = startChar;
}

NSString *reversed = [NSString stringWithCharacters:data length:length];
free(data);


Reverse the string using recursion:

@implementation NSString (Reversed)

+ (NSString *)reversedStringFromString:(NSString *)string
{
    NSUInteger count = [string length];

    if (count <= 1) { // Base Case
        return string;
    } else {
        NSString *lastLetter = [string substringWithRange:NSMakeRange(count - 1, 1)];
        NSString *butLastLetter = [string substringToIndex:count - 1];
        return [lastLetter stringByAppendingString:[self reversedStringFromString:butLastLetter]];
    }
}

@end


Google is your friend:

-(NSString *) reverseString
{
  NSMutableString *reversedStr;
  int len = [self length];

  // Auto released string
  reversedStr = [NSMutableString stringWithCapacity:len];     

  // Probably woefully inefficient...
  while (len > 0)
    [reversedStr appendString:
         [NSString stringWithFormat:@"%C", [self characterAtIndex:--len]]];   

  return reversedStr;
}


None of the answers seem to consider multibyte characters so here is my sample code. It assumes you only ever pass in a string longer than one character.

- (void)testReverseString:(NSString *)string
{
    NSMutableString *rString = [NSMutableString new];
    NSInteger extractChar = [string length] - 1;
    while (extractChar >= 0)
    {
        NSRange oneCharPos = [string rangeOfComposedCharacterSequenceAtIndex:extractChar];
        for (NSUInteger add = 0; add < oneCharPos.length; ++ add)
        {
            unichar oneChar = [string characterAtIndex:oneCharPos.location + add];
            [rString appendFormat:@"%C", oneChar];
        }
        extractChar -= oneCharPos.length;
    }

    NSLog(@"%@ becomes %@", string, encryptedString );
}


  • NSString into char utf32 (always 32 bits (unsigned int))
  • Reverse
  • char utf32 into NSString

+ (NSString *)reverseString3:(NSString *)str {
    unsigned int *cstr, buf, len = [str length], i;  
    cstr  = (unsigned int *)[str cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
    for (i=0;i < len/2;i++) buf = cstr[i], cstr[i] = cstr[len -i-1], cstr[len-i-1] = buf;
    return [[NSString alloc] initWithBytesNoCopy:cstr length:len*4 encoding:NSUTF32LittleEndianStringEncoding freeWhenDone:NO];
}

Example : Apple_is  --->  si_elppA


NSMutableString *result = [NSMutableString stringWithString:@""];
    for (long i = self.length - 1; i >= 0; i--) {
        [result appendFormat:@"%c", [self characterAtIndex:i]];
    }
    return (NSString *)result;


Here is a collection of categories in Objective-C that will reverse both NSStrings and NSAttributedStrings (while preserving character attributes): TextFlipKit

For example:

NSString *example = @"Example Text";

NSString *reversed = example.tfk_reversed;

NSLog(@"Reversed: %@", reversed);

//prints 'Reversed: txeT elpmaxE'


Swift:

let string = "reverse"
let reversedStringCollection = string.characters.reversed()

for character in reversedStringCollection {
    reversedString.append(character)
    print(reversedString)
}


We can also achieve the reverse string as follows.

NSString *originalString = @"Hello";
NSString *reverseString;
for (NSUInteger index = originalString.length; index > 0; index--) {
    char character = [originalString characterAtIndex:index];
    reverseString = [reverseString stringByAppendingString:[NSString stringWithFormat:@"%c", character]];
}

or

NSString *originalString = @"Hello";
NSString *reverseString;
for (NSUInteger index = originalString.length; index > 0; index--) {
   char *character = [originalString characterAtIndex:index];
   reverseString = [reverseString stringByAppendingString:[NSString stringWithFormat:@"%s", character]];
}


Add a category to NSString so you can call reverse on any NSString in the future like this:

#import "NSString+Reverse.h"

@implementation NSString (Reverse)
-(NSString*)reverse {
  char* cstring = (char*)[self UTF8String];
  int length = [self length]-1;

  int i=0;

  while (i<=length) {
    unichar tmp = cstring[i];
    cstring[i] = cstring[length];
    cstring[length] = tmp;
    i++;
    length--;
  }

  return [NSString stringWithCString:cstring encoding:NSUTF8StringEncoding];

}
@end


str=@"india is my countery";
array1=[[NSMutableArray alloc] init];
for(int i =0 ;i<[str length]; i++) {
    NSString *singleCharacter  = [NSString stringWithFormat:@"%c", [str characterAtIndex:i]];
    [array1 addObject:singleCharacter];
}


NSMutableString* theString = [NSMutableString string];

for (int i=[array1 count]-1; i>=0;i--){
    [theString appendFormat:@"%@",[array1 objectAtIndex:i]];
}


I have written a category ove that one :D

//NSString+Reversed.h #import

//
//  NSString+Reversed.h
//  HTMLPageFormatter
//  Created by beit46 on 21.06.13.
//  

@interface NSString (Reversed)
- (NSString *)reversedString;
@end

//NSString+Reversed.m

//
//  NSString+Reversed.m
//  HTMLPageFormatter
//  Created by beit46 on 21.06.13.
#import "NSString+Reversed.h"

@implementation NSString (Reversed)
- (NSString *)reversedString {
    NSMutableString *reversedString = [NSMutableString stringWithCapacity:[self length]];

    [self enumerateSubstringsInRange:NSMakeRange(0,[self length])
                                 options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
                              usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                                  [reversedString appendString:substring];
                              }];
    return [reversedString copy];
}
@end


I have two simple solutions for that purpose:

+(NSString*)reverseString:(NSString *)str
{
    NSMutableString* reversed = [NSMutableString stringWithCapacity:str.length];
    for (int i = (int)str.length-1; i >= 0; i--){
         [reversed appendFormat:@"%c", [str characterAtIndex:i]];
    }
    return reversed;
}

+(NSString*)reverseString2:(NSString *)str
{
    char* cstr = (char*)[str UTF8String];
    int len = (int)str.length;
    for (int i =  0; i < len/2; i++) {
        char buf = cstr[i];
        cstr[i] = cstr[len-i-1];
        cstr[len-i-1] = buf;
    }
    return [[NSString alloc] initWithBytes:cstr length:len encoding:NSUTF8StringEncoding];
}

Now, lets test it!

NSString* str = @"Objective-C is a general-purpose, object-oriented programming language that adds Smalltalk-style messaging to the C programming language";
NSLog(@"REV 1: %@", [Util reverseString:str]);
start = [NSDate date];
for (int i = 0 ; i < 1000; ++i)
    [Util reverseString:str];
end = [NSDate date];
NSLog(@"Time per 1000 repeats: %f", [end timeIntervalSinceDate:start]);

NSLog(@"REV 2: %@", [Util reverseString2:str]);
start = [NSDate date];
for (int i = 0 ; i < 1000; ++i)
    [Util reverseString2:str];
end = [NSDate date];
NSLog(@"Time per 1000 repeats: %f", [end timeIntervalSinceDate:start]);

Results:

ConsoleTestProject[68292:303] REV 1: egaugnal gnimmargorp C eht ot gnigassem elyts-klatllamS sdda taht egaugnal gnimmargorp detneiro-tcejbo ,esoprup-lareneg a si C-evitcejbO
ConsoleTestProject[68292:303] Time per 1000 repeats: 0.063880
ConsoleTestProject[68292:303] REV 2: egaugnal gnimmargorp C eht ot gnigassem elyts-klatllamS sdda taht egaugnal gnimmargorp detneiro-tcejbo ,esoprup-lareneg a si C-evitcejbO
ConsoleTestProject[68292:303] Time per 1000 repeats: 0.002038

And more chars result was:

ConsoleTestProject[68322:303] chars: 1982
ConsoleTestProject[68322:303] Time 1 per 1000 repeats: 1.014893
ConsoleTestProject[68322:303] Time 2 per 1000 repeats: 0.024928

The same text with above functions:

ConsoleTestProject[68366:303] Time 1 per 1000 repeats: 0.873574
ConsoleTestProject[68366:303] Time 2 per 1000 repeats: 0.019300
ConsoleTestProject[68366:303] Time 3 per 1000 repeats: 0.342735 <-Vladimir Gritsenko
ConsoleTestProject[68366:303] Time 4 per 1000 repeats: 0.584012 <- Jano

So, choose performance!

0

精彩评论

暂无评论...
验证码 换一张
取 消