Index: src/OFMutableString.h ================================================================== --- src/OFMutableString.h +++ src/OFMutableString.h @@ -153,10 +153,22 @@ * \param replacement The string with which it should be replaced */ - (void)replaceOccurrencesOfString: (OFString*)string withString: (OFString*)replacement; +/** + * \brief Replaces all occurrences of a string in the specified range with + * another string. + * + * \param string The string to replace + * \param replacement The string with which it should be replaced + * \param range The range in which the string should be replaced + */ +- (void)replaceOccurrencesOfString: (OFString*)string + withString: (OFString*)replacement + inRange: (of_range_t)range; + /** * \brief Deletes all whitespaces at the beginning of the string. */ - (void)deleteLeadingWhitespaces; Index: src/OFMutableString.m ================================================================== --- src/OFMutableString.m +++ src/OFMutableString.m @@ -25,10 +25,11 @@ #import "OFAutoreleasePool.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFNotImplementedException.h" +#import "OFOutOfRangeException.h" #import "macros.h" #import "of_asprintf.h" #import "unicode.h" @@ -410,37 +411,48 @@ atIndex: range.start]; } - (void)replaceOccurrencesOfString: (OFString*)string withString: (OFString*)replacement +{ + [self replaceOccurrencesOfString: string + withString: replacement + inRange: of_range(0, [self length])]; +} + +- (void)replaceOccurrencesOfString: (OFString*)string + withString: (OFString*)replacement + inRange: (of_range_t)range { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init], *pool2; const of_unichar_t *unicodeString; const of_unichar_t *searchString = [string unicodeString]; - size_t length = [self length]; size_t searchLength = [string length]; size_t replacementLength = [replacement length]; size_t i; - if (searchLength > length) { + if (range.start + range.length > [self length]) + @throw [OFOutOfRangeException exceptionWithClass: isa]; + + if (searchLength > range.length) { [pool release]; return; } pool2 = [[OFAutoreleasePool alloc] init]; unicodeString = [self unicodeString]; - for (i = 0; i <= length - searchLength; i++) { + for (i = range.start; i <= range.length - searchLength; i++) { if (memcmp(unicodeString + i, searchString, searchLength * sizeof(of_unichar_t))) continue; [self replaceCharactersInRange: of_range(i, searchLength) withString: replacement]; - length -= searchLength; - length += replacementLength; + range.length -= searchLength; + range.length += replacementLength; i += replacementLength - 1; [pool2 releaseObjects]; Index: src/OFMutableString_UTF8.m ================================================================== --- src/OFMutableString_UTF8.m +++ src/OFMutableString_UTF8.m @@ -537,47 +537,58 @@ s->length = newLength; } - (void)replaceOccurrencesOfString: (OFString*)string withString: (OFString*)replacement + inRange: (of_range_t)range { - const char *UTF8String = [string UTF8String]; - const char *replacementUTF8String = [replacement UTF8String]; - size_t UTF8StringLength = [string UTF8StringLength]; - size_t replacementUTF8StringLength = [replacement UTF8StringLength]; + const char *searchString = [string UTF8String]; + const char *replacementString = [replacement UTF8String]; + size_t searchLength = [string UTF8StringLength]; + size_t replacementLength = [replacement UTF8StringLength]; size_t i, last, newCStringLength, newLength; char *newCString; - if (UTF8StringLength > s->cStringLength) + if (s->UTF8) { + range.start = of_string_index_to_position(s->cString, + range.start, s->cStringLength); + range.length = of_string_index_to_position(s->cString, + range.start + range.length, s->cStringLength) - range.start; + } + + if (range.start + range.length > [self UTF8StringLength]) + @throw [OFOutOfRangeException exceptionWithClass: isa]; + + if ([string UTF8StringLength] > range.length) return; newCString = NULL; newCStringLength = 0; newLength = s->length; - for (i = 0, last = 0; i <= s->cStringLength - UTF8StringLength; i++) { - if (memcmp(s->cString + i, UTF8String, UTF8StringLength)) + for (i = range.start, last = 0; i <= range.length - searchLength; i++) { + if (memcmp(s->cString + i, searchString, searchLength)) continue; @try { newCString = [self resizeMemory: newCString toSize: newCStringLength + i - last + - replacementUTF8StringLength + 1]; + replacementLength + 1]; } @catch (id e) { [self freeMemory: newCString]; @throw e; } memcpy(newCString + newCStringLength, s->cString + last, i - last); memcpy(newCString + newCStringLength + i - last, - replacementUTF8String, replacementUTF8StringLength); + replacementString, replacementLength); - newCStringLength += i - last + replacementUTF8StringLength; + newCStringLength += i - last + replacementLength; newLength = newLength - [string length] + [replacement length]; - i += UTF8StringLength - 1; + i += searchLength - 1; last = i + 1; } @try { newCString = [self Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -649,10 +649,23 @@ * \return A new string with the occurrences of the specified string replaced */ - (OFString*)stringByReplacingOccurrencesOfString: (OFString*)string withString: (OFString*)replacement; +/** + * \brief Creates a new string by replacing the occurrences of the specified + * string in the specified range with the specified replacement. + * + * \param string The string to replace + * \param replacement The string with which it should be replaced + * \param range The range in which to replace the string + * \return A new string with the occurrences of the specified string replaced + */ +- (OFString*)stringByReplacingOccurrencesOfString: (OFString*)string + withString: (OFString*)replacement + inRange: (of_range_t)range; + /** * \brief Returns the string in uppercase. * * \return The string in uppercase */ Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -1391,10 +1391,25 @@ { OFMutableString *new = [[self mutableCopy] autorelease]; [new replaceOccurrencesOfString: string withString: replacement]; + + [new makeImmutable]; + + return new; +} + +- (OFString*)stringByReplacingOccurrencesOfString: (OFString*)string + withString: (OFString*)replacement + inRange: (of_range_t)range +{ + OFMutableString *new = [[self mutableCopy] autorelease]; + + [new replaceOccurrencesOfString: string + withString: replacement + inRange: range]; [new makeImmutable]; return new; } Index: tests/OFStringTests.m ================================================================== --- tests/OFStringTests.m +++ tests/OFStringTests.m @@ -477,10 +477,18 @@ [s[0] isEqual: @"asd foo asd foofoo asd"] && (s[0] = [OFMutableString stringWithString: @"XX"]) && R([s[0] replaceOccurrencesOfString: @"X" withString: @"XX"]) && [s[0] isEqual: @"XXXX"]) + + TEST(@"-[replaceOccurrencesOfString:withString:inRange:]", + (s[0] = [OFMutableString stringWithString: + @"foofoobarfoobarfoo"]) && + R([s[0] replaceOccurrencesOfString: @"oo" + withString: @"óò" + inRange: of_range(2, 15)]) && + [s[0] isEqual: @"foofóòbarfóòbarfoo"]) TEST(@"-[deleteLeadingWhitespaces]", (s[0] = [OFMutableString stringWithString: whitespace[0]]) && R([s[0] deleteLeadingWhitespaces]) && [s[0] isEqual: @"asd \t \t\t\r\n"] &&