2012-04-17 39 views
5

Làm thế nào tôi có thể liệt kê NSString bằng cách kéo từng unichar ra khỏi nó? Tôi có thể sử dụng characterAtIndex nhưng đó là chậm hơn so với làm điều đó bởi một unichar incrementing *. Tôi không thấy bất cứ điều gì trong tài liệu của Apple mà không yêu cầu sao chép chuỗi vào một bộ đệm thứ hai.Liệt kê các ký tự NSString qua con trỏ

Something như thế này sẽ là lý tưởng:

for (unichar c in string) { ... } 

hoặc

unichar* ptr = (unichar*)string; 
+0

Nếu bạn lo lắng về hiệu suất, bạn nên sử dụng NSData và truy cập mảng byte của điều đó. – joerick

+0

Nó chỉ ra rằng CFString thực sự có một cách để làm điều này, trong CFStringGetCharactersP ... –

+2

"... nhưng điều đó sẽ chậm hơn ..." - điều này được gọi là ** tối ưu hóa sớm **. Bạn đang đưa ra giả định về hiệu suất ngay cả trước khi bạn biết liệu hiệu suất có phải là một vấn đề hay không. Bạn nên thực hiện nó một cách rõ ràng (sử dụng 'characterAtIndex') và tối ưu hóa nó chỉ khi bạn có vấn đề về hiệu năng. – Sulthan

Trả lời

11

Bạn có thể tăng tốc độ -characterAtIndex: bằng cách chuyển đổi nó để nó IMP hình thức đầu tiên:

NSString *str = @"This is a test"; 

NSUInteger len = [str length]; // only calling [str length] once speeds up the process as well 
SEL sel = @selector(characterAtIndex:); 

// using typeof to save my fingers from typing more 
unichar (*charAtIdx)(id, SEL, NSUInteger) = (typeof(charAtIdx)) [str methodForSelector:sel]; 

for (int i = 0; i < len; i++) { 
    unichar c = charAtIdx(str, sel, i); 
    // do something with C 
    NSLog(@"%C", c); 
} 

EDIT: Dường như CFString Reference chứa các phương pháp sau đây:

const UniChar *CFStringGetCharactersPtr(CFStringRef theString); 

Điều này có nghĩa bạn có thể làm như sau:

const unichar *chars = CFStringGetCharactersPtr((__bridge CFStringRef) theString); 

while (*chars) 
{ 
    // do something with *chars 
    chars++; 
} 

Nếu bạn không muốn phân bổ m emory để đối phó với bộ đệm, đây là con đường để đi.

+0

Tìm kiếm tốt, nhưng từ phần Giá trị trả về: "Một con trỏ tới bộ đệm của ký tự Unicode hoặc NULL nếu bộ nhớ trong của chuỗi không cho phép điều này được trả về hiệu quả". Điều này sẽ nhanh nhất, nhưng vẫn cần một bản sao lưu chỉ trong trường hợp. – ughoavgfhw

+0

Rực rỡ, tôi không nghĩ đến việc sử dụng CF ... API, nhưng đó là một ý tưởng tuyệt vời. Hoạt động tuyệt vời. – jjxtra

+0

@ughoavgfhw đúng, rất đúng, nó cần một bản sao lưu. Nhưng đối với những gì OP muốn, điều này sẽ làm việc tốt. –

0

này sẽ làm việc:

char *s = [string UTF8String]; 
for (char *t = s; *t; t++) 
    /* use as */ *t; 

[Chỉnh sửa] Và nếu bạn thực sự cần các ký tự unicode sau đó bạn có không có tùy chọn nào khác ngoài việc sử dụng chiều dàicharacterAtIndex. Từ tài liệu:

Lớp NSString có hai phương thức nguyên thủy — chiều dài và ký tựAtIndex: —đây là cơ sở cho tất cả các phương pháp khác trong giao diện của nó. Phương thức chiều dài trả về tổng số ký tự Unicode trong chuỗi. characterAtIndex: cho phép truy cập tới từng nhân vật trong chuỗi bởi chỉ số, với các giá trị chỉ số bắt đầu từ 0.

Vì vậy, mã của bạn sẽ là:

for (int index = 0; index < string.length; index++) 
    { 
     unichar c = [string characterAtIndex: index]; 
     /* ... */ 
    } 

[sửa 2]

Ngoài ra, don Đừng quên rằng NSString là 'cầu nối miễn phí' với CFString và do đó tất cả các chức năng giao diện C-mã thẳng, không có mục tiêu-C đều có thể sử dụng được. Người liên quan sẽ là CFStringGetCharacterAtIndex

+0

Điều đó chỉ hoạt động đối với các điểm mã unicode nhỏ hơn 128. Ngay sau khi bạn gặp phải một ký tự bit cao, nó sẽ bị ngắt. Ngoài ra, rất có thể sẽ tạo một bản sao dữ liệu thứ hai, mà người hỏi đang cố gắng tránh. – grahamparks

+0

Tôi cho rằng điều này đòi hỏi phải sao chép utf-8 byte bằng cách nào đó? Con trỏ đó sống ở đâu? Là NSString utf-8 bên dưới? – jjxtra

+0

Chuỗi C được tạo. Tài liệu cho UTF8String: _ Chuỗi C trả về được tự động giải phóng giống như một đối tượng trả về sẽ được giải phóng; bạn nên sao chép chuỗi C nếu nó cần lưu trữ nó bên ngoài ngữ cảnh autorelease trong đó chuỗi C được tạo._ – GoZoner

0

Tôi không nghĩ bạn có thể làm điều này. NSString là một giao diện trừu tượng cho vô số các lớp không đảm bảo về lưu trữ nội bộ của dữ liệu ký tự, do đó hoàn toàn có thể không có mảng ký tự nào để nhận con trỏ tới.

Nếu không có tùy chọn nào được đề cập trong câu hỏi phù hợp với ứng dụng của bạn, tôi khuyên bạn nên tạo lớp chuỗi của riêng bạn cho mục đích này hoặc sử dụng mảng unichar thô malloc'ed thay vì đối tượng chuỗi.

4

Tùy chọn duy nhất của bạn là sao chép các ký tự vào bộ đệm mới. Điều này là do lớp NSString không đảm bảo rằng có một bộ đệm bên trong mà bạn có thể sử dụng. Cách tốt nhất để làm điều này là sử dụng phương pháp getCharacters:range:.

Nếu bạn đang sử dụng các chuỗi có khả năng rất dài, sẽ tốt hơn để phân bổ bộ đệm kích thước cố định và liệt kê chuỗi theo khối (đây thực sự là cách liệt kê nhanh).

+0

Hmmm. Tôi tự hỏi nếu characterAtIndex nhanh hơn cho rằng nó không phải sao chép bộ nhớ ... suy nghĩ? – jjxtra

+3

Có thể, nhưng không chắc. Chi phí của việc gọi một phương thức cho mỗi nhân vật sẽ nhanh chóng vượt qua chi phí của việc ghi vào bộ nhớ khi kích thước của bộ đệm tăng lên. Trừ khi tất nhiên bạn đang sử dụng một lớp NSString tùy chỉnh không cung cấp phương thức 'getCharacters: range:' tối ưu. – ughoavgfhw

+0

@PsychoDad Tôi nghĩ rằng việc sử dụng '-characterAtIndex:' * có thể * nhanh hơn, nếu bạn bỏ qua phần trên của thời gian chạy objc, và đơn giản là sử dụng hàm C. –

1

Tôi đã tạo phương pháp liệt kê kiểu khối sử dụng getCharacters:range: với bộ đệm có kích thước cố định, theo đề xuất của ughoavgfhw trong câu trả lời của mình. Nó tránh tình huống mà CFStringGetCharactersPtr trả về null và nó không phải malloc một bộ đệm lớn. Bạn có thể thả nó vào một thể loại NSString, hoặc sửa đổi nó để lấy một chuỗi như một tham số nếu bạn muốn.

-(void)enumerateCharactersWithBlock:(void (^)(unichar, NSUInteger, BOOL *))block 
{ 
    const NSInteger bufferSize = 16; 
    const NSInteger length = [self length]; 
    unichar buffer[bufferSize]; 
    NSInteger bufferLoops = (length - 1)/bufferSize + 1; 
    BOOL stop = NO; 
    for (int i = 0; i < bufferLoops; i++) { 
     NSInteger bufferOffset = i * bufferSize; 
     NSInteger charsInBuffer = MIN(length - bufferOffset, bufferSize); 
     [self getCharacters:buffer range:NSMakeRange(bufferOffset, charsInBuffer)]; 
     for (int j = 0; j < charsInBuffer; j++) { 
      block(buffer[j], j + bufferOffset, &stop); 
      if (stop) { 
       return; 
      } 
     } 
    } 
} 
+0

Công trình này, nhưng sẽ không nhanh như con trỏ thô nguyên bản – jjxtra

+0

Đúng, nhưng như tôi đã nói, điều này xử lý trường hợp mà CFStringGetCharactersPtr trả về null. – Aaron

Các vấn đề liên quan