Thay thế nhóm chụp Regex bằng chữ hoa trong Javascript


80

Tôi muốn biết cách thay thế một nhóm nắm bắt bằng chữ hoa của nó trong JavaScript. Đây là một phiên bản đơn giản của những gì tôi đã thử cho đến nay nhưng không hoạt động:

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'

Bạn có thể giải thích điều gì sai với mã này không?


@Erik không xóa một thành phần của câu hỏi. Tôi muốn biết tại sao mã của tôi cũng không thành công.
Evan Carroll

Evan, tôi nghĩ tôi tôn trọng câu hỏi của bạn. Tôi chỉ loại bỏ những thứ dường như không cần thiết. Vì bạn đã cung cấp mã bạn đang thử và rõ ràng là nó không hoạt động, nên mọi người ngầm hiểu rằng bạn cần một lời giải thích tại sao mà bạn không cần phải nói như vậy (và thật khó xử). Chỉ cố gắng giúp đỡ! :)
ErikE

1
Evan, có tốt hơn không? Tôi không có ý làm phiền. Nếu bạn khôi phục lại, tôi sẽ không chỉnh sửa lại, nhưng ít nhất bạn có thể giữ nguyên các chỉnh sửa tiêu đề và thẻ không?
ErikE

Về mặt kỹ thuật, tôi không sử dụng Javascript chút nào, tôi đang sử dụng v8 (ECMAScript). Nhưng, tôi tưởng tượng hầu hết mọi người đang tìm kiếm điều này sẽ tìm kiếm JavaScript, vì vậy tôi rất tốt với nó.
Evan Carroll

1
Hãy thêm lại thẻ nếu bạn nghĩ rằng chúng thuộc về.
ErikE

Câu trả lời:


158

Bạn có thể chuyển một hàm cho replace.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });

Giải trình

a.replace( /(f)/, "$1".toUpperCase())

Trong ví dụ này, bạn chuyển một chuỗi vào hàm thay thế. Vì bạn đang sử dụng cú pháp thay thế đặc biệt ($ N lấy lần chụp thứ N), bạn chỉ đơn giản là đưa ra cùng một giá trị. Thực toUpperCasesự lừa dối vì bạn chỉ tạo chuỗi thay thế viết hoa (Điều này hơi vô nghĩa vì ký tự $và một 1không có chữ hoa nên giá trị trả về sẽ vẫn là "$1") .

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))

Tin hay không thì ngữ nghĩa của biểu thức này hoàn toàn giống nhau.


@Evan Carroll: Vui lòng xem câu trả lời của tôi.
kay

4
À, tôi hiểu ý bạn, tôi đang tăng "\ $ 1". Không phải kết quả của voodoo mà thay thế sẽ làm mà rõ ràng là thay thế $1cho nhóm bắt đầu tiên.
Evan Carroll

@EvanCarroll để được giải thích cặn kẽ về lý do tại sao mã ban đầu của bạn không hoạt động và cách làm cho nó hoạt động, hãy xem câu trả lời của tôi bên dưới.
Joshua Piccari

15

Tôi biết tôi đến muộn bữa tiệc nhưng đây là một phương pháp ngắn hơn phù hợp với những nỗ lực ban đầu của bạn.

a.replace('f', String.call.bind(a.toUpperCase));

Vậy bạn đã sai ở đâu và voodoo mới này là gì?

Vấn đề 1

Như đã nêu trước đây, bạn đang cố gắng chuyển các kết quả của một phương thức được gọi làm tham số thứ hai của String.prototype.replace () , thay vào đó, bạn nên chuyển một tham chiếu đến một hàm

Giải pháp 1

Điều đó đủ dễ dàng để giải quyết. Đơn giản chỉ cần loại bỏ các tham số và dấu ngoặc đơn sẽ cung cấp cho chúng ta một tham chiếu hơn là thực thi hàm.

a.replace('f', String.prototype.toUpperCase.apply)

Vấn đề 2

Nếu bạn cố gắng chạy mã ngay bây giờ, bạn sẽ gặp lỗi cho biết rằng undefined không phải là một hàm và do đó không thể được gọi. Điều này là do String.prototype.toUpperCase.apply thực sự là một tham chiếu đến Function.prototype.apply () thông qua kế thừa nguyên mẫu của JavaScript. Vì vậy, những gì chúng tôi đang thực sự làm trông giống như thế này

a.replace('f', Function.prototype.apply)

Đó rõ ràng không phải là những gì chúng tôi đã dự định. Làm thế nào nó biết để chạy Function.prototype.apply () trên String.prototype.toUpperCase () ?

Giải pháp 2

Sử dụng Function.prototype.bind (), chúng ta có thể tạo một bản sao của Function.prototype.call với ngữ cảnh của nó được đặt cụ thể thành String.prototype.toUpperCase. Bây giờ chúng tôi có những thứ sau

a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))

Vấn đề 3

Vấn đề cuối cùng là String.prototype.replace () sẽ truyền một số đối số cho hàm thay thế của nó. Tuy nhiên, Function.prototype.apply () mong muốn tham số thứ hai là một mảng nhưng thay vào đó nhận một chuỗi hoặc số (tùy thuộc vào việc bạn có sử dụng nhóm nắm bắt hay không). Điều này sẽ gây ra lỗi danh sách đối số không hợp lệ.

Giải pháp 3

May mắn thay, chúng ta chỉ có thể thay thế trong Function.prototype.call () (chấp nhận bất kỳ số lượng đối số nào, không có đối số nào có giới hạn kiểu) cho Function.prototype.apply () . Bây giờ chúng tôi đã đến mã làm việc!

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))

Bỏ từng byte!

Không ai muốn nhập nguyên mẫu nhiều lần. Thay vào đó, chúng ta sẽ tận dụng thực tế là chúng ta có các đối tượng tham chiếu đến các phương thức giống nhau thông qua kế thừa. Phương thức khởi tạo Chuỗi, là một hàm, kế thừa từ nguyên mẫu của Hàm. Điều này có nghĩa là chúng ta có thể thay thế trong String.call cho Function.prototype.call (thực ra chúng ta có thể sử dụng Date.call để tiết kiệm nhiều byte hơn nhưng ít ngữ nghĩa hơn).

Chúng tôi cũng có thể tận dụng biến 'a' của mình vì nguyên mẫu của nó bao gồm một tham chiếu đến String.prototype.toUpperCase, chúng tôi có thể hoán đổi biến đó với a.toUpperCase. Đó là sự kết hợp của 3 giải pháp ở trên và các biện pháp tiết kiệm byte này là cách chúng tôi lấy mã ở đầu bài đăng này.


4
Bạn đã lưu 8 ký tự, trong khi làm mờ mã theo cách để yêu cầu một trang giải thích về giải pháp rõ ràng hơn. Tôi không tin rằng đây là một chiến thắng.
Lawrence Dol,

1
Về mặt trí tuệ, đây là một giải pháp tuyệt vời, trong đó nó hiển thị / dạy một hoặc hai điều về các hàm javascript. Nhưng tôi với Lawrence rằng trong thực tế, nó quá mù mờ để thực sự được sử dụng. Vẫn tuyệt.
meetamit

9

Bài cũ nhưng đáng để mở rộng câu trả lời @ChaosPandion cho các trường hợp sử dụng khác với RegEx bị hạn chế hơn. Ví dụ: đảm bảo (f)hoặc chụp xung quanh nhóm với một định dạng cụ thể /z(f)oo/:

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.

Tôi chỉ muốn làm nổi bật hai tham số của functionviệc tìm kiếm một định dạng cụ thể và thay thế một nhóm chụp trong định dạng có thể.


Cảm ơn bạn! Các bài viết trước dường như đã giải đáp vấn đề tại sao mã của OP không hoạt động trong khi hoàn toàn bỏ qua những gì có vẻ là điểm thực sự đối với tôi - thay thế một nhóm đối sánh!
Auspex 19/02/19

Tôi nghĩ rằng bạn có một lỗi trong chức năng thay thế của bạn, nhưng hãy kiểm tra tôi về điều này. Tôi nghĩ rằng nó nên là return $0.replace($0, $1.toUpperCase()), đâu $0là đối số đầu tiên
Mike

Đó là một chuỗi đơn giản để thay thế chuỗi. vì vậy f đến F là đúng.
CallMeLaNN

Điều này thực sự hữu ích nếu bạn đang cố gắng thay thế một cái gì đó trong ngoặc!
Diode Dan

3

Tại sao chúng ta không chỉ tra cứu định nghĩa ?

Nếu chúng ta viết:

a.replace(/(f)/, x => x.toUpperCase())

chúng tôi cũng có thể chỉ nói:

a.replace('f','F')

Tệ hơn nữa, tôi nghi ngờ rằng không ai nhận ra rằng các ví dụ của họ đã hoạt động chỉ vì họ đã nắm bắt toàn bộ regex bằng dấu ngoặc đơn. Nếu bạn nhìn vào định nghĩa , tham số đầu tiên được truyền cho replacerhàm thực sự là toàn bộ mẫu đã khớp và không phải là mẫu bạn đã chụp bằng dấu ngoặc đơn:

function replacer(match, p1, p2, p3, offset, string)

Nếu bạn muốn sử dụng ký hiệu hàm mũi tên:

a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()

1

GIẢI PHÁP

a.replace(/(f)/,x=>x.toUpperCase())  

để thay thế tất cả các lần xuất hiện grup, hãy sử dụng /(f)/gregexp. Vấn đề trong mã của bạn: String.prototype.toUpperCase.apply("$1")"$1".toUpperCase()đưa ra "$1"(tự mình thử trong bảng điều khiển) - vì vậy nó không thay đổi bất cứ điều gì và trên thực tế, bạn gọi hai lần a.replace( /(f)/, "$1")(cũng không thay đổi gì).


0

Đưa ra một từ điển (đối tượng, trong trường hợp này là a Map) thuộc tính, giá trị và cách sử dụng .bind()như được mô tả trong câu trả lời

const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]); 
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));

console.log(str);

Sử dụng một đối tượng thuần JavaScript và với một hàm được xác định để trả về giá trị thuộc tính phù hợp của đối tượng hoặc chuỗi gốc nếu không tìm thấy kết quả phù hợp nào

const regex = /([A-z0-9]+)/;
const dictionary = {
  "hello": 123,
  [Symbol("dictionary")](prop) {
    return this[prop] || prop
  }
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));

console.log(str);

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.