Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create a Regex from a string matching this string literally (with escaping) #31989

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ export
# search, find, match and related functions
eachmatch,
endswith,
escape_regex,
findall,
findfirst,
findlast,
Expand Down
30 changes: 29 additions & 1 deletion base/regex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ function hash(r::Regex, h::UInt)
h = hash(r.match_options, h)
end

## String operations ##
## string operations ##

"""
*(s::Regex, t::Union{Regex,AbstractString,AbstractChar}) -> Regex
Expand Down Expand Up @@ -710,3 +710,31 @@ RegexMatch("Test Test ")
```
"""
^(r::Regex, i::Integer) = Regex(string("(?:", r.pattern, "){$i}"), r.compile_options, r.match_options)

## conversion from strings/chars ##

"""
escape_regex(x::AbstractChar)
escape_regex(x::AbstractString)

Create a `Regex` which matches `x` exactly. This means that special characters are
not interpreted as having special meaning.
Multiplication can also be used in order to create a regex with specific flags.

!!! compat "Julia 1.3"
This method requires at least Julia 1.3.

# Examples
```jldoctest
julia> match(escape_regex('('), "(").match
"("

julia> match(escape_regex(".*"), "abc") == nothing
true

julia> match(escape_regex(".*"), ".*")
RegexMatch(".*")
```

"""
escape_regex(s::Union{AbstractString,AbstractChar}) = Regex(wrap_string(s, zero(UInt32)))
1 change: 1 addition & 0 deletions doc/src/base/strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Base.isvalid(::Any, ::Any)
Base.isvalid(::AbstractString, ::Integer)
Base.match
Base.eachmatch
Base.escape_regex
Base.isless(::AbstractString, ::AbstractString)
Base.:(==)(::AbstractString, ::AbstractString)
Base.cmp(::AbstractString, ::AbstractString)
Expand Down
19 changes: 19 additions & 0 deletions test/regex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,25 @@
@test r"this|that"^2 == r"(?:this|that){2}"
end

@testset "escape_regex" begin
r = escape_regex('a')
@test r == r"\Qa\E" # these tests can change if the implementation changes
@test match(r, "a").match == "a"
r = escape_regex('\\')
@test r == r"\Q\\E"
@test match(r, "\\").match == "\\"
r = escape_regex('(')
@test r == r"\Q(\E"
@test match(r, "(").match == "("

r = escape_regex("a\\b(c")
@test r == r"\Qa\b(c\E"
@test match(r, "a\\b(c").match == "a\\b(c"
r = escape_regex("a\\E\\Qz")
@test r == r"\Qa\\E\QE\Qz\E"
@test match(r, "a\\E\\Qz") != nothing
end

# Test that PCRE throws the correct kind of error
# TODO: Uncomment this once the corresponding change has propagated to CI
#@test_throws ErrorException Base.PCRE.info(C_NULL, Base.PCRE.INFO_NAMECOUNT, UInt32)
Expand Down