2009-12-17 123 views
15

我在寻找一种简单,有效的方式转换为字符串首字母大写强调符号(即MyClassName - > my_class_name)和目的C.首字母大写,下划线和回用Objective-C

再次回到

我目前的解决方案涉及很多rangeOfStringcharacterAtIndex和对NSMutableStrings的操作,并且就像地狱一样简单丑陋:)似乎必须有更好的解决方案,但我不确定它是什么。

我宁愿不为这个用例导入一个正则表达式库,尽管如果所有其他的失败都是这样。

回答

10

Chris对RegexKitLite的建议很好。这是一个很好的工具包,但这可以通过NSScanner轻松完成。在+uppercaseLetterCharacterSet+lowercaseLetterCharacterSet之间交替使用-scanCharactersFromSet:intoString:。返回时,您可以使用-scanUpToCharactersFromSet:来代替,只需使用一个字符集,其中只有一个下划线。

+1

感谢罗布 - 我缺乏使用NSScanner的经验,这让我忽略了这个解决方案,但它比我拥有的要干净得多。 – 2009-12-17 13:10:49

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

编辑:一个快速谷歌搜索后,我找不到这样做的任何方式,即使在平原。然而,我没有找到一个框架,可能是有用的。它被称为RegexKitLite。它使用内置的ICU库,因此它只为最终的二进制文件增加了大约20K。

+1

克里斯,感谢多的RegexKitLite指针。我一定会在未来的项目中使用它! – 2009-12-17 13:11:20

+0

它比在iOS开发条款中发布这个答案迟了约10万年;应该有其他人在这里绊倒:不要使用RegexKitLite。在发布这个答案约六个月后,iOS 4中出现了“NSRegularExpression”,将相同的ICU库引入标准框架。 – Tommy 2015-11-10 21:03:03

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。

4

这是我实现罗布的回答:

@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 
+0

为什么不使用NSMutableString作为构建器? – 2013-08-05 21:21:19

+0

确实会更好。 :-) – MonsieurDart 2013-08-21 09:09:21

0

我已经联合在这里找到了我的重构库,es_ios_utils答案。见NSCategories.h

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

用法:

@"my_string".asCamelCaseFromUnderscores 

产量@ “的myString”

请把改进!

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

'previousChar'永远不会被读取并且可以被删除。 – 2014-07-01 08:32:09

0

我偶然发现了这个问题寻找一种方式,以驼峰式转换为间隔,用户可显示的字符串。这里是我的解决方案,它的工作比@代替@ “_””“

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

看起来已经很不错了,但是:1.如果输入为空,该怎么办? 2.如何转换回来工作? 3.你的循环不能以'i = 1'开始,使'i> 1'过时? – Trinimon 2013-03-02 10:44:36

+0

1.如果输入为零,则返回零,因为发送给零的长度消息将返回0 2.好的一点,我不需要这个在我的应用程序 3.我喜欢这一点,编辑它到我的答案 – 2013-03-02 11:55:27

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

如果该字符串是帕斯卡情况下,即MyBundleVersion,它会产生_my_bundle_version – 2014-08-11 06:34:13

+0

我很想看到一个固定的前导下划线和一个转换另一种方式的版本。 – 2015-08-11 17:41:28

+1

@PeterDeWeese,像[这一个](https://regex101.com/r/zM4dD2/1)? – 2016-02-05 13:21:01

0

OK家伙。下面是一个所有正则表达式的答案,这是我考虑的唯一正确的方法:

考虑:

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

\ 1然而大号$,奇怪的是,不与自身的小写版本替换第一捕获组:(不确定为什么,它应该工作:/

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