2009-12-17 63 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; 
}