CamelCase强调并回到Objective-C

时间:2009-12-17 01:43:32

标签: objective-c string nsstring

我正在寻找一种简单有效的方法将CamelCase中的字符串转换为下划线表示法(即MyClassName - > my_class_name),然后再回到Objective C中。

我当前的解决方案涉及NSMutableStrings上的大量rangeOfStringcharacterAtIndexreplaceCharactersInRange操作,并且只是简单丑陋:)似乎必须有更好的解决方案,但我不确定它是什么。

我不想仅针对这一个用例导入正则表达式库,但如果所有其他用例都失败,那么这是一个选项。

10 个答案:

答案 0 :(得分:10)

Chris对RegexKitLite的建议很好。这是一个很好的工具包,但这可以通过NSScanner轻松完成。在-scanCharactersFromSet:intoString:+uppercaseLetterCharacterSet之间轮流使用+lowercaseLetterCharacterSet。要返回,您可以使用-scanUpToCharactersFromSet:代替,使用仅包含下划线的字符集。

答案 1 :(得分:9)

这些怎么样:

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。

答案 2 :(得分:9)

试试这个魔法:

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

答案 3 :(得分:4)

如果您关注的只是代码的可见性,则可以使用您已设计的方法为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
     }
}

编辑:经过快速谷歌搜索,我找不到任何方法,即使在简单的C中也是如此。但是,我找到了一个可能有用的框架。它被称为RegexKitLite。它使用内置的ICU库,因此它只为最终的二进制文件增加了大约20K。

答案 4 :(得分:4)

这是我对Rob的回答的实现:

@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

答案 5 :(得分:3)

这是基于以上所有内容的另一个版本。此版本处理其他表单。特别是,测试如下:

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;
}

答案 6 :(得分:1)

如果您关心代码的速度,您可能希望编写更高性能的代码版本:

- (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;
}

答案 7 :(得分:0)

我已将此处找到的答案合并到我的重构库es_ios_utils中。见NSCategories.h

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

用法:

@"my_string".asCamelCaseFromUnderscores

收益@“myString”

请推动改进!

答案 8 :(得分:0)

我遇到了这个问题,寻找一种方法将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;
}

答案 9 :(得分:0)

好的伙计们。这是一个全正则表达式的答案,我认为这是唯一真正的方法:

假设:

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然而,奇怪的是,并没有用自己的小写版本替换第一个捕获组:(不知道为什么,它应该工作。:/