2011-12-11 35 views
5

Tôi muốn chuyển đổi văn bản sau đâyThay regex phần phù hợp tại chỗ với Ruby

This is a ![foto](foto.jpeg), here is another ![foto](foto.png) 

vào

This is a ![foto](/folder1/foto.jpeg), here is another ![foto](/folder2/foto.png) 

Nói cách khác tôi muốn tìm tất cả các đường dẫn hình ảnh được kèm theo trong ngoặc đơn (văn bản nằm trong cú pháp Markdown) và thay thế chúng bằng các đường dẫn khác. Chuỗi chứa đường dẫn mới được trả về bởi hàm real_path riêng biệt.

Tôi muốn thực hiện việc này bằng cách sử dụng String#gsub trong phiên bản chặn của nó. Hiện nay mã của tôi trông như thế này:

re = /!\[.*?\]\((.*?)\)/ 

rel_content = content.gsub(re) do |path| 
    real_path(path) 
end 

Vấn đề với regex này là nó sẽ phù hợp ![foto](foto.jpeg) thay vì chỉ foto.jpeg. Tôi cũng đã thử các regexen khác như (?>\!\[.*?\]\()(.*?)(?>\)) nhưng không có kết quả.

Giải pháp hiện tại của tôi là chia nhỏ đường dẫn và lắp ráp lại sau.

Có một regex Ruby chỉ khớp với đường dẫn bên trong dấu ngoặc và không phải tất cả các ký tự bắt buộc theo ngữ cảnh không?

Cập nhật sau câu trả lời: Vấn đề chính ở đây là regexen của Ruby không có cách nào để chỉ định lookbehinds không có chiều rộng. Giải pháp chung nhất là nhóm các phần của regexp trước và phần sau phần khớp thực, tức là /(pre)(matching-part)(post)/ và tạo lại chuỗi đầy đủ sau đó.

Trong trường hợp này các giải pháp sẽ được

re = /(!\[.*?\]\()(.*?)(\))/ 

rel_content = content.gsub(re) do 
    $1 + real_path($2) + $3 
end 

Trả lời

5

Một giải pháp nhanh chóng (điều chỉnh khi cần thiết):

s = 'This is a ![foto](foto.jpeg)' 

s.sub!(/!(\[.*?\])\((.*?)\)/, '\1(/folder1/\2)') 

p s # This is a [foto](/folder1/foto.jpeg) 
+0

Có lẽ ý tưởng tốt nhất là, như bạn đề nghị, để tiết kiệm phần trước và sau khi trận đấu thực sự trong nhóm riêng biệt và xây dựng lại chuỗi cuối cùng với '$ 1 + real_path ($ 2) + $ 3'. – gioele

3

Trong khối, sử dụng $1 để truy cập nhóm chụp đầu tiên ($2 cho phần thứ hai và vân vân).

Từ các tài liệu:

Trong hình thức khối, chuỗi trận đấu hiện nay được thông qua tại như một tham số, và các biến như $ 1, $ 2, $ `, $ &, và $' sẽ được thiết lập thích hợp. Giá trị được trả về bởi khối sẽ được thay thế cho trận đấu trên mỗi cuộc gọi.

4

Bạn luôn có thể làm điều đó trong hai bước - đầu tiên trích xuất các biểu hiện hình ảnh toàn bộ ra ngoài và sau đó thứ hai thay thế liên kết:

str = "This is a ![foto](foto.jpeg), here is another ![foto](foto.png)" 

str.gsub(/\!\[[^\]]*\]\(([^)]*)\)/) do |image| 
    image.gsub(/(?<=\()(.*)(?=\))/) do |link| 
    "https://stackoverflow.com/a/new/path/" + link 
    end 
end 

#=> "This is a ![foto](/a/new/path/foto.jpeg), here is another ![foto](/a/new/path/foto.png)" 

Tôi đã thay đổi regex đầu tiên một chút, nhưng bạn có thể sử dụng cùng một regex bạn đã có trước đó ở vị trí của nó. image là biểu thức hình ảnh như ![foto](foto.jpeg)link chỉ là đường dẫn như foto.jpeg.

[EDIT] Làm rõ: Ruby không có lookbehinds (và chúng được sử dụng trong câu trả lời của tôi):

Bạn có thể tạo lookbehinds với (?<=regex) cho dương và (?<!regex) cho tiêu cực, nơi regex là một regex tùy ý biểu thức tùy thuộc vào điều kiện sau. Các biểu thức Regexp trong lookbehinds chúng phải được cố định chiều rộng do các hạn chế về việc thực hiện regex, có nghĩa là chúng không thể bao gồm các biểu thức với một số lần lặp lại hoặc các thay đổi với các lựa chọn độ rộng khác nhau. Nếu bạn cố gắng làm điều đó, bạn sẽ gặp lỗi. (Hạn chế không áp dụng cho lookaheads mặc dù).

Trong trường hợp của bạn, phần [foto] có chiều rộng thay đổi (foto có thể là bất kỳ chuỗi nào) do đó, nó không thể đi vào giao diện do ở trên. Tuy nhiên, lookbehind là chính xác những gì chúng ta cần vì nó là một trận đấu không rộng, và chúng ta tận dụng lợi thế đó trong regex thứ hai mà chỉ cần lo lắng về (cố định chiều dài) bắt buộc mở ngoặc đơn.

Rõ ràng bạn có thể đặt real_path từ đây, nhưng tôi chỉ muốn một ví dụ có thể kiểm tra.

Tôi nghĩ rằng phương pháp này là linh hoạt hơn và dễ đọc hơn so xây dựng lại chuỗi thông qua các biến nhóm trận đấu

+0

Đây là những gì tôi đang làm bây giờ. Tôi hy vọng rằng có một số cú pháp tôi không biết để tạo ra lookbehinds trong Regexps của Ruby: ( – gioele

+0

Bạn _can_ làm lookbehinds trong ruby, nhưng có một điều kiện. – arcresu

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