Skip to content

Commit 2896759

Browse files
extend escape_raw_string to allow specifying the delimiter (#52001)
Similar to how `escape_raw_string` enabled users to reverse the transform done by string literals, this enables users to reverse the transform done by cmd literals by specifying an argument of a '\`'. Refs #41041 Co-authored-by: Simeon Schaub <[email protected]>
1 parent bc0d888 commit 2896759

File tree

3 files changed

+32
-17
lines changed

3 files changed

+32
-17
lines changed

base/strings/io.jl

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -612,14 +612,14 @@ julia> println(raw"\\\\x \\\\\\"")
612612
macro raw_str(s); s; end
613613

614614
"""
615-
escape_raw_string(s::AbstractString)
616-
escape_raw_string(io, s::AbstractString)
615+
escape_raw_string(s::AbstractString, delim='"') -> AbstractString
616+
escape_raw_string(io, s::AbstractString, delim='"')
617617
618618
Escape a string in the manner used for parsing raw string literals.
619-
For each double-quote (`"`) character in input string `s`, this
620-
function counts the number _n_ of preceding backslash (`\\`) characters,
621-
and then increases there the number of backslashes from _n_ to 2_n_+1
622-
(even for _n_ = 0). It also doubles a sequence of backslashes at the end
619+
For each double-quote (`"`) character in input string `s` (or `delim` if
620+
specified), this function counts the number _n_ of preceding backslash (`\\`)
621+
characters, and then increases there the number of backslashes from _n_ to
622+
2_n_+1 (even for _n_ = 0). It also doubles a sequence of backslashes at the end
623623
of the string.
624624
625625
This escaping convention is used in raw strings and other non-standard
@@ -629,36 +629,41 @@ command-line string into the argv[] array.)
629629
630630
See also [`escape_string`](@ref).
631631
"""
632-
function escape_raw_string(io, str::AbstractString)
632+
function escape_raw_string(io::IO, str::AbstractString, delim::Char='"')
633+
total = 0
633634
escapes = 0
634635
for c in str
635636
if c == '\\'
636637
escapes += 1
637638
else
638-
if c == '"'
639+
if c == delim
639640
# if one or more backslashes are followed by
640641
# a double quote then escape all backslashes
641642
# and the double quote
642-
escapes = escapes * 2 + 1
643-
end
644-
while escapes > 0
645-
write(io, '\\')
646-
escapes -= 1
643+
escapes += 1
644+
total += escapes
645+
while escapes > 0
646+
write(io, '\\')
647+
escapes -= 1
648+
end
647649
end
648650
escapes = 0
649-
write(io, c)
650651
end
652+
write(io, c)
651653
end
652654
# also escape any trailing backslashes,
653655
# so they do not affect the closing quote
656+
total += escapes
654657
while escapes > 0
655-
write(io, '\\')
656658
write(io, '\\')
657659
escapes -= 1
658660
end
661+
total
662+
end
663+
function escape_raw_string(str::AbstractString, delim::Char='"')
664+
total = escape_raw_string(devnull, str, delim) # check whether the string even needs to be copied and how much to allocate for it
665+
return total == 0 ? str : sprint(escape_raw_string, str, delim; sizehint = sizeof(str) + total)
659666
end
660-
escape_raw_string(str::AbstractString) = sprint(escape_raw_string, str;
661-
sizehint = lastindex(str) + 2)
662667

663668
## multiline strings ##
664669

doc/src/base/strings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,6 @@ Base.isspace
9595
Base.isuppercase
9696
Base.isxdigit
9797
Base.escape_string
98+
Base.escape_raw_string
9899
Base.unescape_string
99100
```

test/strings/io.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@
156156
@test "aaa \\g \\n" == unescape_string(str, ['g', 'n'])
157157
end
158158
@test Base.escape_raw_string(raw"\"\\\"\\-\\") == "\\\"\\\\\\\"\\\\-\\\\"
159+
@test Base.escape_raw_string(raw"`\`\\-\\") == "\`\\\`\\\\-\\\\"
160+
@test Base.escape_raw_string(raw"\"\\\"\\-\\", '`') == "\"\\\"\\\\-\\\\"
161+
@test Base.escape_raw_string(raw"`\`\\-\\", '`') == "\\\`\\\\\\\`\\\\-\\\\"
162+
@test Base.escape_raw_string(raw"some`string") == "some`string"
163+
@test Base.escape_raw_string(raw"some\"string", '`') == "some\"string"
164+
@test Base.escape_raw_string(raw"some`string\\") == "some`string\\\\"
165+
@test Base.escape_raw_string(raw"some\"string\\", '`') == "some\"string\\\\"
166+
@test Base.escape_raw_string(raw"some\"string") == "some\\\"string"
167+
@test Base.escape_raw_string(raw"some`string", '`') == "some\\`string"
159168
end
160169
@testset "join()" begin
161170
@test join([]) == join([],",") == ""

0 commit comments

Comments
 (0)