OLD | NEW |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package regexp | 5 package regexp |
6 | 6 |
7 import ( | 7 import ( |
| 8 "bytes" |
| 9 "io" |
8 "os" | 10 "os" |
9 "strings" | 11 "strings" |
10 "testing" | 12 "testing" |
| 13 "utf8" |
11 ) | 14 ) |
12 | 15 |
13 var good_re = []string{ | 16 var good_re = []string{ |
14 ``, | 17 ``, |
15 `.`, | 18 `.`, |
16 `^.$`, | 19 `^.$`, |
17 `a`, | 20 `a`, |
18 `a*`, | 21 `a*`, |
19 `a+`, | 22 `a+`, |
20 `a?`, | 23 `a?`, |
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 ReplaceTest{"abc", "", "abcdabc", "d"}, | 298 ReplaceTest{"abc", "", "abcdabc", "d"}, |
296 ReplaceTest{"x", "xXx", "xxxXxxx", "xXxxXxxXxXxXxxXxxXx"}, | 299 ReplaceTest{"x", "xXx", "xxxXxxx", "xXxxXxxXxXxXxxXxxXx"}, |
297 ReplaceTest{"abc", "d", "", ""}, | 300 ReplaceTest{"abc", "d", "", ""}, |
298 ReplaceTest{"abc", "d", "abc", "d"}, | 301 ReplaceTest{"abc", "d", "abc", "d"}, |
299 ReplaceTest{".+", "x", "abc", "x"}, | 302 ReplaceTest{".+", "x", "abc", "x"}, |
300 ReplaceTest{"[a-c]*", "x", "def", "xdxexfx"}, | 303 ReplaceTest{"[a-c]*", "x", "def", "xdxexfx"}, |
301 ReplaceTest{"[a-c]+", "x", "abcbcdcdedef", "xdxdedef"}, | 304 ReplaceTest{"[a-c]+", "x", "abcbcdcdedef", "xdxdedef"}, |
302 ReplaceTest{"[a-c]*", "x", "abcbcdcdedef", "xdxdxexdxexfx"}, | 305 ReplaceTest{"[a-c]*", "x", "abcbcdcdedef", "xdxdxexdxexfx"}, |
303 } | 306 } |
304 | 307 |
| 308 type ReplaceFuncTest struct { |
| 309 pattern string |
| 310 replacement func(string)string |
| 311 input, output string |
| 312 } |
| 313 |
| 314 var replaceFuncTests = []ReplaceFuncTest{ |
| 315 ReplaceFuncTest{"[a-c]", func(s string) string { return "x" + s + "y" },
"defabcdef", "defxayxbyxcydef"}, |
| 316 ReplaceFuncTest{"[a-c]+", func(s string) string { return "x" + s + "y" }
, "defabcdef", "defxabcydef"}, |
| 317 ReplaceFuncTest{"[a-c]*", func(s string) string { return "x" + s + "y" }
, "defabcdef", "xydxyexyfxabcydxyexyfxy"}, |
| 318 } |
| 319 |
305 func TestReplaceAll(t *testing.T) { | 320 func TestReplaceAll(t *testing.T) { |
306 for _, tc := range replaceTests { | 321 for _, tc := range replaceTests { |
307 re, err := Compile(tc.pattern) | 322 re, err := Compile(tc.pattern) |
308 if err != nil { | 323 if err != nil { |
309 t.Errorf("Unexpected error compiling %q: %v", tc.pattern
, err) | 324 t.Errorf("Unexpected error compiling %q: %v", tc.pattern
, err) |
310 continue | 325 continue |
311 } | 326 } |
312 actual := re.ReplaceAllString(tc.input, tc.replacement) | 327 actual := re.ReplaceAllString(tc.input, tc.replacement) |
313 if actual != tc.output { | 328 if actual != tc.output { |
314 t.Errorf("%q.Replace(%q,%q) = %q; want %q", | 329 t.Errorf("%q.Replace(%q,%q) = %q; want %q", |
315 tc.pattern, tc.input, tc.replacement, actual, tc
.output) | 330 tc.pattern, tc.input, tc.replacement, actual, tc
.output) |
316 } | 331 } |
317 // now try bytes | 332 // now try bytes |
318 actual = string(re.ReplaceAll([]byte(tc.input), []byte(tc.replac
ement))) | 333 actual = string(re.ReplaceAll([]byte(tc.input), []byte(tc.replac
ement))) |
319 if actual != tc.output { | 334 if actual != tc.output { |
320 t.Errorf("%q.Replace(%q,%q) = %q; want %q", | 335 t.Errorf("%q.Replace(%q,%q) = %q; want %q", |
321 tc.pattern, tc.input, tc.replacement, actual, tc
.output) | 336 tc.pattern, tc.input, tc.replacement, actual, tc
.output) |
322 } | 337 } |
323 } | 338 } |
324 } | 339 } |
325 | 340 |
| 341 func TestReplaceAllFunc(t *testing.T) { |
| 342 for _, tc := range replaceFuncTests { |
| 343 re, err := Compile(tc.pattern) |
| 344 if err != nil { |
| 345 t.Errorf("Unexpected error compiling %q: %v", tc.pattern
, err) |
| 346 continue |
| 347 } |
| 348 actual := re.ReplaceAllStringFunc(tc.input, tc.replacement) |
| 349 if actual != tc.output { |
| 350 t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q", |
| 351 tc.pattern, tc.input, tc.replacement, actual, tc
.output) |
| 352 } |
| 353 // now try bytes |
| 354 actual = string(re.ReplaceAllFunc([]byte(tc.input), func(s []byt
e) []byte { return []byte(tc.replacement(string(s))) } )) |
| 355 if actual != tc.output { |
| 356 t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q", |
| 357 tc.pattern, tc.input, tc.replacement, actual, tc
.output) |
| 358 } |
| 359 } |
| 360 } |
| 361 |
326 type QuoteMetaTest struct { | 362 type QuoteMetaTest struct { |
327 pattern, output string | 363 pattern, output string |
328 } | 364 } |
329 | 365 |
330 var quoteMetaTests = []QuoteMetaTest{ | 366 var quoteMetaTests = []QuoteMetaTest{ |
331 QuoteMetaTest{``, ``}, | 367 QuoteMetaTest{``, ``}, |
332 QuoteMetaTest{`foo`, `foo`}, | 368 QuoteMetaTest{`foo`, `foo`}, |
333 QuoteMetaTest{`!@#$%^&*()_+-=[{]}\|,<.>/?~`, `!@#\$%\^&\*\(\)_\+-=\[{\]}
\\\|,<\.>/\?~`}, | 369 QuoteMetaTest{`!@#$%^&*()_+-=[{]}\|,<.>/?~`, `!@#\$%\^&\*\(\)_\+-=\[{\]}
\\\|,<\.>/\?~`}, |
334 } | 370 } |
335 | 371 |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
503 b.StopTimer() | 539 b.StopTimer() |
504 re, _ := Compile("^" + x) | 540 re, _ := Compile("^" + x) |
505 b.StartTimer() | 541 b.StartTimer() |
506 for i := 0; i < b.N; i++ { | 542 for i := 0; i < b.N; i++ { |
507 if !re.MatchString(x) { | 543 if !re.MatchString(x) { |
508 println("no match!") | 544 println("no match!") |
509 break | 545 break |
510 } | 546 } |
511 } | 547 } |
512 } | 548 } |
| 549 |
| 550 func BenchmarkReplaceAll(b *testing.B) { |
| 551 x := "abcdefghijklmnopqrstuvwxyz" |
| 552 b.StopTimer() |
| 553 re, _ := Compile("[cjrw]") |
| 554 b.StartTimer() |
| 555 for i := 0; i < b.N; i++ { |
| 556 re.ReplaceAllString(x, "") |
| 557 } |
| 558 } |
| 559 |
| 560 func BenchmarkOldReplaceAll(b *testing.B) { |
| 561 x := "abcdefghijklmnopqrstuvwxyz" |
| 562 b.StopTimer() |
| 563 re, _ := Compile("[cjrw]") |
| 564 b.StartTimer() |
| 565 for i := 0; i < b.N; i++ { |
| 566 re.oldReplaceAllString(x, "") |
| 567 } |
| 568 } |
| 569 |
| 570 func (re *Regexp) oldReplaceAllString(src, repl string) string { |
| 571 lastMatchEnd := 0 // end position of the most recent match |
| 572 searchPos := 0 // position where we next look for a match |
| 573 buf := new(bytes.Buffer) |
| 574 for searchPos <= len(src) { |
| 575 a := re.doExecute(src, nil, searchPos) |
| 576 if len(a) == 0 { |
| 577 break // no more matches |
| 578 } |
| 579 |
| 580 // Copy the unmatched characters before this match. |
| 581 io.WriteString(buf, src[lastMatchEnd:a[0]]) |
| 582 |
| 583 // Now insert a copy of the replacement string, but not for a |
| 584 // match of the empty string immediately after another match. |
| 585 // (Otherwise, we get double replacement for patterns that |
| 586 // match both empty and nonempty strings.) |
| 587 if a[1] > lastMatchEnd || a[0] == 0 { |
| 588 io.WriteString(buf, repl) |
| 589 } |
| 590 lastMatchEnd = a[1] |
| 591 |
| 592 // Advance past this match; always advance at least one characte
r. |
| 593 _, width := utf8.DecodeRuneInString(src[searchPos:]) |
| 594 if searchPos+width > a[1] { |
| 595 searchPos += width |
| 596 } else if searchPos+1 > a[1] { |
| 597 // This clause is only needed at the end of the input |
| 598 // string. In that case, DecodeRuneInString returns wid
th=0. |
| 599 searchPos++ |
| 600 } else { |
| 601 searchPos = a[1] |
| 602 } |
| 603 } |
| 604 |
| 605 // Copy the unmatched characters after the last match. |
| 606 io.WriteString(buf, src[lastMatchEnd:]) |
| 607 |
| 608 return buf.String() |
| 609 } |
| 610 |
OLD | NEW |