2010-10-22 44 views
27

Tôi có một chuỗi CamelCase đẹp như ImageWideNice hoặc ImageNarrowUgly. Bây giờ tôi muốn phá vỡ chuỗi đó trong các chất nền của nó, chẳng hạn như Image, Wide hoặc NarrowNice hoặc Ugly.Làm thế nào để tách một chuỗi CamelCase trong các chuỗi của nó trong Ruby?

Tôi nghĩ điều này có thể được giải quyết đơn giản bằng cách

camelCaseString =~ /(Image)((Wide)|(Narrow))((Nice)|(Ugly))/ 

Nhưng kỳ lạ, điều này sẽ chỉ điền $1$2, nhưng không $3.

Bạn có ý tưởng hay hơn để tách chuỗi đó không?

+1

Bạn muốn làm gì với 'ThisIsANarrowImageOfHIV?' Tham gia với n hoặc chia nhỏ HIV? –

Trả lời

50
s = 'nowIsTheTime' 

s.split /(?=[A-Z])/ 

=> ["now", "Is", "The", "Time"] 

?=pattern là ví dụ về lookahead tích cực. Về bản chất, nó khớp với một điểm trong chuỗi ngay trước mẫu . Nó không tiêu thụ các ký tự, nghĩa là, nó không bao gồm mẫu như là một phần của trận đấu. Một ví dụ khác:

irb> 'streets'.sub /t(?=s)/, '-' 
=> "stree-s" 

Trong trường hợp này s được kết hợp (chỉ t trận đấu thứ hai) nhưng không được thay thế. Nhờ @Bryceregexp doc link. Bryce Anderson của ông thêm một lời giải thích:

Các ?= vào đầu của nhóm () trận đấu được gọi là dương lookahead, mà chỉ là một cách khác để nói rằng trong khi regex là tìm tại các nhân vật trong việc xác định liệu nó có phù hợp hay không, nó không phải là khiến chúng trở thành một phần của trận đấu. split() thường ăn ở giữa các ký tự ở giữa , nhưng trong trường hợp này, đối sánh chính nó trống, do đó, không có không có gì [ở đó].

+1

Bạn đã thử 'NowIsTheTime' chưa? – splash

+1

@splash: nó vẫn hoạt động tốt – ryeguy

+0

Trong các thử nghiệm của tôi regex này dẫn đến '[" "," Now "," Is "," The "," Time "]' nếu chữ cái đầu tiên là một chữ hoa. Tôi làm gì sai? – splash

2

Các bạn đã thử

camelCaseString =~ /(Image)(Wide|Narrow)(Nice|Ugly)/ 

?

2

tổ chức sự kiện dù đây là một regex câu hỏi Ruby và các answer by DigitalRoss là chính xác và tỏa sáng bởi sự đơn giản của nó, tôi muốn thêm một câu trả lời Java:

// this regex doesn't work perfect with Java and other regex engines 
"NowIsTheTime".split("(?=[A-Z])"); // ["", "Now", "Is", "The", "Time"] 

// this regex works with first uppercase or lowercase characters 
"NowIsTheTime".split("(?!(^|[a-z]|$))"); // ["Now", "Is", "The", "Time"] 
"nowIsTheTime".split("(?!(^|[a-z]|$))"); // ["now", "Is", "The", "Time"] 
27

Tôi biết điều này là cũ, nhưng đáng nói cho những người khác có thể đang tìm kiếm điều này. Trong đường ray bạn có thể thực hiện việc này: "NowIsTheTime".underscore.humanize

5

Câu trả lời của DigitalRoss là chính xác vì nó xử lý trường hợp chung mà bạn không biết là trường hợp lạc đà nghiêm ngặt (chữ thường đầu tiên) hay trường hợp Pascal (chữ hoa đầu tiên).

Nếu bạn biết chuỗi nào trong số này tạo thành chuỗi hoặc bạn muốn ép buộc chuỗi này hoặc chuỗi kia, Inflector có thể thực hiện.

Đối với trường hợp Pascal:

"NowIsTheTime".titleize 

Đối với trường hợp lạc đà:

"nowIsTheTime".titleize.camelize :lower 
+0

Quan trọng cần lưu ý, '# titleize' và' # camelize' là các phương thức Rails nghiêm ngặt, chứ không phải trong lõi Ruby. – onebree

0

Câu trả lời từ DigitalRoss sẽ không nhận ra từ viết tắt nhúng trong CamelCase. Ví dụ: nó sẽ tách "MyHTMLTricks" thành "My H T M L Tricks" thay vì "My HTML Tricks".

Dưới đây là một lựa chọn dựa trên AsSpaced() chức năng trong PmWiki, mà làm một công việc tuyệt vời của việc nhạy cảm với các trường hợp như thế này:

"MyHTMLTricks" \ 
.gsub(/([[:lower:]\\d])([[:upper:]])/, '\1 \2') \ 
.gsub(/([^-\\d])(\\d[-\\d]*(|$))/,'\1 \2') \ 
.gsub(/([[:upper:]])([[:upper:]][[:lower:]\\d])/, '\1 \2') 

=> "My HTML Tricks" 

Một điều khác tôi thích về cách tiếp cận này là nó rời khỏi chuỗi một chuỗi, thay vì biến nó thành một mảng. Nếu bạn thực sự muốn mảng, sau đó chỉ cần thêm một phần ở cuối.

"MyHTMLTricks" \ 
.gsub(/([[:lower:]\\d])([[:upper:]])/, '\1 \2') \ 
.gsub(/([^-\\d])(\\d[-\\d]*(|$))/,'\1 \2') \ 
.gsub(/([[:upper:]])([[:upper:]][[:lower:]\\d])/, '\1 \2') \ 
.split 

=> ["My", "HTML", "Tricks"] 

Để lưu nội dung, đây là mã PHP gốc từ PmWiki.

function AsSpaced($text) { 
    $text = preg_replace("/([[:lower:]\\d])([[:upper:]])/", '$1 $2', $text); 
    $text = preg_replace('/([^-\\d])(\\d[-\\d]*(|$))/', '$1 $2', $text); 
    return preg_replace("/([[:upper:]])([[:upper:]][[:lower:]\\d])/", '$1 $2', $text); 
} 
Các vấn đề liên quan