2009-12-17 23 views
15

Tôi đang tìm kiếm một cách hiệu quả đơn giản để chuyển đổi chuỗi trong CamelCase để nhấn ký hiệu (ví dụ, MyClassName -> my_class_name) và ngược lại trong Objective C.CamelCase để dấu gạch dưới và ngược lại trong Objective-C

My giải pháp hiện tại liên quan đến rất nhiều các hoạt động rangeOfString, characterAtIndex và trên NSMutableStrings và chỉ đơn giản là xấu xí như địa ngục :) Dường như phải có giải pháp tốt hơn, nhưng tôi không chắc nó là gì.

Tôi không muốn nhập thư viện regex chỉ cho trường hợp sử dụng này, mặc dù đó là tùy chọn nếu mọi thứ khác không thành công.

Trả lời

10

Đề xuất của Chris về RegexKitLite là tốt. Đó là một bộ công cụ tuyệt vời, nhưng điều này có thể được thực hiện khá dễ dàng với NSScanner. Sử dụng -scanCharactersFromSet:intoString: luân phiên giữa +uppercaseLetterCharacterSet+lowercaseLetterCharacterSet. Để quay lại, thay vào đó, bạn sử dụng -scanUpToCharactersFromSet:, sử dụng bộ ký tự chỉ với dấu gạch dưới trong đó.

+1

Cảm ơn Rob - sự thiếu kinh nghiệm của tôi khi sử dụng NSScanner là điều khiến tôi bỏ qua giải pháp này, nhưng nó sạch hơn rất nhiều so với những gì tôi có. –

4

Nếu mối quan tâm của bạn chỉ là khả năng hiển thị mã của bạn, bạn có thể tạo một danh mục cho NSString bằng cách sử dụng các phương pháp bạn đã thiết kế. Bằng cách đó, bạn chỉ nhìn thấy những mớ hỗn độn xấu xí một lần. ;)

Ví dụ:

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

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

EDIT: Sau khi tìm kiếm trên Google nhanh chóng, tôi không thể tìm thấy bất cứ cách nào để làm điều này, ngay cả trong đồng bằng C. Tuy nhiên, tôi đã tìm thấy một khuôn khổ có thể hữu ích. Nó được gọi là RegexKitLite. Nó sử dụng thư viện ICU tích hợp, vì vậy nó chỉ thêm khoảng 20K vào nhị phân cuối cùng.

+1

Chris, nhờ nhiều cho con trỏ RegexKitLite. Tôi chắc chắn sẽ sử dụng nó trong các dự án trong tương lai! –

+0

khoảng 100.000 năm sau khi câu trả lời này được đăng trong các điều khoản phát triển iOS; nên bất cứ ai khác vấp ngã ở đây: không sử dụng RegexKitLite. 'NSRegularExpression' được bật lên trong iOS 4, khoảng sáu tháng sau khi câu trả lời này được đăng, đưa cùng một thư viện ICU vào các khung tiêu chuẩn. – Tommy

9

Làm thế nào về những:

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

Một số nhược điểm là chúng làm sử dụng chuỗi tạm thời để chuyển đổi giữa chữ hoa và chữ, và họ không có bất kỳ logic cho từ viết tắt, vì vậy myURL sẽ dẫn đến my_u_r_l.

4

Dưới đây là thực hiện của tôi về câu trả lời của 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 
+0

tại sao không sử dụng NSMutableString làm người xây dựng? –

+0

Sẽ tốt hơn, thực sự. :-) – MonsieurDart

0

tôi đã kết hợp các câu trả lời tìm thấy ở đây vào thư viện refactoring tôi, es_ios_utils. Xem NSCategories.h:

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

Cách sử dụng:

@"my_string".asCamelCaseFromUnderscores 

sản lượng @ "myString"

hãy đẩy những cải tiến!

3

Đây là một phiên bản khác dựa trên tất cả những điều trên. Phiên bản này xử lý các biểu mẫu bổ sung. Đặc biệt, kiểm tra như sau:

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

Ở đây đi

- (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' không bao giờ được đọc và có thể bị xóa. –

0

tôi tình cờ khi câu hỏi này tìm kiếm một cách để chuyển đổi Camel Trường hợp đến một khoảng cách đều nhau, sử dụng chuỗi thể hiển thị.Đây là giải pháp của tôi mà làm việc tốt hơn so với thay thế @ "_" với @""

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

Có vẻ khá tốt, nhưng: 1. nếu đầu vào là null thì sao? 2. chuyển đổi hoạt động như thế nào? 3. vòng lặp của bạn không thể bắt đầu bằng 'i = 1' làm cho' i> 1'obsolete? – Trinimon

+0

1. Nếu đầu vào là nil thì nil được trả về khi tin nhắn độ dài được gửi đến số 0 sẽ trả về 0 2. Tốt, tôi không cần điều này trong ứng dụng 3. Tôi thích điều đó, chỉnh sửa nó vào câu trả lời của tôi –

9

Hãy thử kỳ diệu này:

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

Output: my_bundle_version

+3

Nếu chuỗi là trong trường hợp Pascal tức là MyBundleVersion, nó sẽ tạo ra _my_bundle_version –

+0

Tôi rất thích nhìn thấy một phiên bản có dấu gạch dưới hàng đầu cố định và một phiên bản chuyển đổi theo cách khác. –

+1

@PeterDeChúng tôi, như [this one] (https://regex101.com/r/zM4dD2/1)? –

0

OK guys. Dưới đây là một câu trả lời tất cả các regex, mà tôi xem xét các cách đúng chỉ:

Given:

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"]; 

sản lượng foobar.

Ngược lại:

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

Sản lượng: foo_bar.

\ U $ 2 thay thế nhóm chụp thứ hai với chữ hoa phiên bản của bản thân: D

\ tuy nhiên L $ 1, kỳ quặc, không thay thế cho nhóm chụp đầu tiên với một phiên bản thấp hơn trường hợp của bản thân :(Không chắc tại sao, nó cũng làm việc:./

1

Nếu bạn đang quan tâm đến tốc độ của mã của bạn có thể bạn muốn viết một phiên bản performant hơn của mã:

- (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; 
} 
Các vấn đề liên quan