CamelCase для подчеркивания и обратно в Objective-C - PullRequest
15 голосов
/ 17 декабря 2009

Я ищу простой и эффективный способ преобразования строк в CamelCase в нотацию с подчеркиванием (т.е. MyClassName -> my_class_name) и обратно в Objective C.

Мое текущее решение включает в себя множество операций rangeOfString, characterAtIndex и replaceCharactersInRange над NSMutableStrings, и это просто ужасно чертовски :) Кажется, должно быть лучшее решение, но я не уверен что это.

Я бы предпочел не импортировать библиотеку регулярных выражений только для этого одного варианта использования, хотя это вариант, если все остальное терпит неудачу.

Ответы [ 10 ]

10 голосов
/ 17 декабря 2009

Предложение Криса о RegexKitLite - это хорошо. Это отличный инструментарий, но это можно сделать довольно легко с помощью NSScanner . Используйте -scanCharactersFromSet:intoString:, чередуя между +uppercaseLetterCharacterSet и +lowercaseLetterCharacterSet. Для возврата назад вы должны использовать -scanUpToCharactersFromSet: вместо набора символов с подчеркиванием.

9 голосов
/ 08 ноября 2013

Попробуйте эту магию:

NSString* camelCaseString = @"myBundleVersion";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])" options:0 error:nil];
NSString *underscoreString = [[regex stringByReplacingMatchesInString:camelCaseString options:0 range:NSMakeRange(0, camelCaseString.length) withTemplate:@"_$1$2"] lowercaseString];
NSLog(@"%@", underscoreString);

Вывод: my_bundle_version

9 голосов
/ 17 декабря 2009

Как насчет этих:

NSString *MyCamelCaseToUnderscores(NSString *input) {
    NSMutableString *output = [NSMutableString string];
    NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
    for (NSInteger idx = 0; idx < [input length]; idx += 1) {
        unichar c = [input characterAtIndex:idx];
        if ([uppercase characterIsMember:c]) {
            [output appendFormat:@"_%@", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
        } else {
            [output appendFormat:@"%C", c];
        }
    }
    return output;
}

NSString *MyUnderscoresToCamelCase(NSString *underscores) {
    NSMutableString *output = [NSMutableString string];
    BOOL makeNextCharacterUpperCase = NO;
    for (NSInteger idx = 0; idx < [underscores length]; idx += 1) {
        unichar c = [underscores characterAtIndex:idx];
        if (c == '_') {
            makeNextCharacterUpperCase = YES;
        } else if (makeNextCharacterUpperCase) {
            [output appendString:[[NSString stringWithCharacters:&c length:1] uppercaseString]];
            makeNextCharacterUpperCase = NO;
        } else {
            [output appendFormat:@"%C", c];
        }
    }
    return output;
}

Некоторые недостатки заключаются в том, что они используют временные строки для преобразования между прописными и строчными буквами и не имеют никакой логики для сокращений, поэтому myURL приведет к my_u_r_l.

4 голосов
/ 29 ноября 2010

Вот моя реализация ответа Роба:

@implementation NSString (CamelCaseConversion)

// Convert a camel case string into a dased word sparated string.
// In case of scanning error, return nil.
// Camel case string must not start with a capital.
- (NSString *)fromCamelCaseToDashed {

    NSScanner *scanner = [NSScanner scannerWithString:self];
    scanner.caseSensitive = YES;

    NSString *builder = [NSString string];
    NSString *buffer = nil;
    NSUInteger lastScanLocation = 0;

    while ([scanner isAtEnd] == NO) {

        if ([scanner scanCharactersFromSet:[NSCharacterSet lowercaseLetterCharacterSet] intoString:&buffer]) {

            builder = [builder stringByAppendingString:buffer];

            if ([scanner scanCharactersFromSet:[NSCharacterSet uppercaseLetterCharacterSet] intoString:&buffer]) {

                builder = [builder stringByAppendingString:@"-"];
                builder = [builder stringByAppendingString:[buffer lowercaseString]];
            }
        }

        // If the scanner location has not moved, there's a problem somewhere.
        if (lastScanLocation == scanner.scanLocation) return nil;
        lastScanLocation = scanner.scanLocation;
    }

    return builder;
}

@end
4 голосов
/ 17 декабря 2009

Если вас беспокоит только видимость вашего кода, вы можете создать категорию для NSString, используя методы, которые вы уже разработали. Таким образом, вы видите только один ужасный беспорядок. ;)

Например:

@interface NSString(Conversions) {
     - (NSString *)asCamelCase;
     - (NSString *)asUnderscored;
}

@implementation NSString(Conversions) {
     - (NSString *)asCamelCase {
          // whatever you came up with
     }
     - (NSString *)asUnderscored {
          // whatever you came up with
     }
}

РЕДАКТИРОВАТЬ : После быстрого поиска в Google я не смог найти никакого способа сделать это, даже на простом C. Однако я нашел систему, которая может быть полезной. Он называется RegexKitLite . Он использует встроенную библиотеку ICU, поэтому добавляет к окончательному двоичному файлу только около 20 КБ.

3 голосов
/ 13 июля 2012

Вот еще одна версия, основанная на всем вышеупомянутом. Эта версия обрабатывает дополнительные формы. В частности, проверено со следующим:

camelCase => camel_case
camelCaseWord => camel_case_word
camelURL => camel_url
camelURLCase => camel_url_case
CamelCase => camel_case

А вот и

- (NSString *)fromCamelCaseToDashed3 {
    NSMutableString *output = [NSMutableString string];
    NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
    BOOL previousCharacterWasUppercase = FALSE;
    BOOL currentCharacterIsUppercase = FALSE;
    unichar currentChar = 0;
    unichar previousChar = 0;
    for (NSInteger idx = 0; idx < [self length]; idx += 1) {
        previousChar = currentChar;
        currentChar = [self characterAtIndex:idx];
        previousCharacterWasUppercase = currentCharacterIsUppercase;
        currentCharacterIsUppercase = [uppercase characterIsMember:currentChar];

        if (!previousCharacterWasUppercase && currentCharacterIsUppercase && idx > 0) {
            // insert an _ between the characters
            [output appendString:@"_"];
        } else if (previousCharacterWasUppercase && !currentCharacterIsUppercase) {
            // insert an _ before the previous character
            // insert an _ before the last character in the string
            if ([output length] > 1) {
                unichar charTwoBack = [output characterAtIndex:[output length]-2];
                if (charTwoBack != '_') {
                    [output insertString:@"_" atIndex:[output length]-1];
                }
            }
        } 
        // Append the current character lowercase
        [output appendString:[[NSString stringWithCharacters:&currentChar length:1] lowercaseString]];
    }
    return output;
}
1 голос
/ 29 сентября 2016

Если вас беспокоит скорость вашего кода, вы, вероятно, захотите написать более производительную версию кода:

- (nonnull NSString *)camelCaseToSnakeCaseString {
    if ([self length] == 0) {
        return @"";
    }
    NSMutableString *output = [NSMutableString string];
    NSCharacterSet *digitSet = [NSCharacterSet decimalDigitCharacterSet];
    NSCharacterSet *uppercaseSet = [NSCharacterSet uppercaseLetterCharacterSet];
    NSCharacterSet *lowercaseSet = [NSCharacterSet lowercaseLetterCharacterSet];

    for (NSInteger idx = 0; idx < [self length]; idx += 1) {
        unichar c = [self characterAtIndex:idx];

        // if it's the last one then just append lowercase of character
        if (idx == [self length] - 1) {
            if ([uppercaseSet characterIsMember:c]) {
                [output appendFormat:@"%@", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
            }
            else {
                [output appendFormat:@"%C", c];
            }
            continue;
        }

        unichar nextC = [self characterAtIndex:(idx+1)];
        // this logic finds the boundaries between lowercase/uppercase/digits and lets the string be split accordingly.
        if ([lowercaseSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
            [output appendFormat:@"%@_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
        }
        else if ([lowercaseSet characterIsMember:c] && [digitSet characterIsMember:nextC]) {
            [output appendFormat:@"%@_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
        }
        else if ([digitSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
            [output appendFormat:@"%@_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
        }
        else {
            // Append lowercase of character
            if ([uppercaseSet characterIsMember:c]) {
                [output appendFormat:@"%@", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
            }
            else {
                [output appendFormat:@"%C", c];
            }
        }
    }
    return output;
}
0 голосов
/ 06 января 2016

ОК, ребята. Вот ответ на все регулярные выражения, который я считаю единственным верным способом:

Дано:

NSString *MYSTRING = "foo_bar";

NSRegularExpression *_toCamelCase = [NSRegularExpression 
    regularExpressionWithPattern:@"(_)([a-z])" 
    options:NSRegularExpressionCaseInsensitive error:&error];

NSString *camelCaseAttribute = [_toCamelCase 
    stringByReplacingMatchesInString:MYSTRING options:0 
    range:NSMakeRange(0, attribute.length) 
    withTemplate:@"\\U$2"];

Выход fooBar .

И наоборот:

NSString *MYSTRING = "fooBar";


NSRegularExpression *camelCaseTo_ = [NSRegularExpression 
    regularExpressionWithPattern:@"([A-Z])" 
    options:0 error:&error];

NSString *underscoreParsedAttribute = [camelCaseTo_ 
    stringByReplacingMatchesInString:MYSTRING 
    options:0 range:NSMakeRange(0, attribute.length) 
    withTemplate:@"_$1"];
underscoreParsedAttribute = [underscoreParsedAttribute lowercaseString];

Выход: foo_bar .

\ U $ 2 заменяет вторую группу захвата самой версией в верхнем регистре: D

\ L $ 1, однако, как ни странно, первая группа захвата не заменяется строчной версией самой себя :( Не знаю почему, она должна работать.: /

0 голосов
/ 02 марта 2013

Я случайно наткнулся на этот вопрос, пытаясь найти способ преобразовать Camel Case в разнесенную пользователем строку. Вот мое решение, которое сработало лучше, чем замена @ "_" на @ ""

- (NSString *)fromCamelCaseToSpaced:(NSString*)input {
    NSCharacterSet* lower = [NSCharacterSet lowercaseLetterCharacterSet];
    NSCharacterSet* upper = [NSCharacterSet uppercaseLetterCharacterSet];

    for (int i = 1; i < input.length; i++) {
        if ([upper characterIsMember:[input characterAtIndex:i]] &&
            [lower characterIsMember:[input characterAtIndex:i-1]])
        {
            NSString* soFar = [input substringToIndex:i];
            NSString* left = [input substringFromIndex:i];
            return [NSString stringWithFormat:@"%@ %@", soFar, [self fromCamelCaseToSpaced:left]];
        }
    }
    return input;
}
0 голосов
/ 08 июня 2011

Я объединил ответы, найденные здесь, в мою библиотеку рефакторинга, es_ios_utils . См. NSCategories.h :

@property(nonatomic, readonly) NSString *asCamelCaseFromUnderscores;
@property(nonatomic, readonly) NSString *asUnderscoresFromCamelCase;

Использование:

@"my_string".asCamelCaseFromUnderscores

приводит к @ "myString"

Пожалуйста, нажмите улучшения!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...