fmt: debugging formats for characters: %+q %#U
%+q uses strconv.Quote[Rune]ToASCII, guaranteeing ASCII-only output.
%#U a quoted character if the rune is printable: 'x'=U+0078; otherwise
it's as before: U+000A.
R=golang-dev, gri, rsc
CC=golang-dev
https://golang.org/cl/4589047
diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go
index 79fe575..35a11e1 100644
--- a/src/pkg/fmt/doc.go
+++ b/src/pkg/fmt/doc.go
@@ -63,11 +63,13 @@
number of characters to output, truncating if necessary.
Other flags:
- + always print a sign for numeric values
+ + always print a sign for numeric values;
+ guarantee ASCII-only output for %q (%+q)
- pad with spaces on the right rather than the left (left-justify the field)
# alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
0X for hex (%#X); suppress 0x for %p (%#p);
- print a raw (backquoted) string if possible for %q (%#q)
+ print a raw (backquoted) string if possible for %q (%#q);
+ write e.g. U+0078 'x' if the character is printable for %U (%#U).
' ' (space) leave a space for elided sign in numbers (% d);
put spaces between bytes printing strings or slices in hex (% x, % X)
0 pad with leading zeros rather than spaces
diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go
index 122b951..3d255c3 100644
--- a/src/pkg/fmt/fmt_test.go
+++ b/src/pkg/fmt/fmt_test.go
@@ -133,6 +133,7 @@
{"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`},
{"%q", "abc\xffdef", `"abc\xffdef"`},
{"%q", "\u263a", `"☺"`},
+ {"%+q", "\u263a", `"\u263a"`},
{"%q", "\U0010ffff", `"\U0010ffff"`},
// escaped characters
@@ -145,6 +146,8 @@
{"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`},
{"%q", '"', `'"'`},
{"%q", '\'', `'\''`},
+ {"%q", "\u263a", `"☺"`},
+ {"%+q", "\u263a", `"\u263a"`},
// width
{"%5s", "abc", " abc"},
@@ -187,6 +190,12 @@
{"%U", 0x12345, "U+12345"},
{"%10.6U", 0xABC, " U+000ABC"},
{"%-10.6U", 0xABC, "U+000ABC "},
+ {"%U", '\n', `U+000A`},
+ {"%#U", '\n', `U+000A`},
+ {"%U", 'x', `U+0078`},
+ {"%#U", 'x', `U+0078 'x'`},
+ {"%U", '\u263a', `U+263A`},
+ {"%#U", '\u263a', `U+263A '☺'`},
// floats
{"%+.3e", 0.0, "+0.000e+00"},
diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go
index 5dcfb96..bec55f7 100644
--- a/src/pkg/fmt/format.go
+++ b/src/pkg/fmt/format.go
@@ -7,6 +7,7 @@
import (
"bytes"
"strconv"
+ "unicode"
"utf8"
)
@@ -50,6 +51,7 @@
sharp bool
space bool
unicode bool
+ uniQuote bool // Use 'x'= prefix for %U if printable.
zero bool
}
@@ -63,6 +65,7 @@
f.sharp = false
f.space = false
f.unicode = false
+ f.uniQuote = false
f.zero = false
}
@@ -232,6 +235,24 @@
i--
buf[i] = ' '
}
+
+ // If we want a quoted char for %#U, move the data up to make room.
+ if f.unicode && f.uniQuote && a >= 0 && a <= unicode.MaxRune && unicode.IsPrint(int(a)) {
+ runeWidth := utf8.RuneLen(int(a))
+ width := 1 + 1 + runeWidth + 1 // space, quote, rune, quote
+ copy(buf[i-width:], buf[i:]) // guaranteed to have enough room.
+ i -= width
+ // Now put " 'x'" at the end.
+ j := len(buf) - width
+ buf[j] = ' '
+ j++
+ buf[j] = '\''
+ j++
+ utf8.EncodeRune(buf[j:], int(a))
+ j += runeWidth
+ buf[j] = '\''
+ }
+
f.pad(buf[i:])
}
@@ -291,7 +312,11 @@
if f.sharp && strconv.CanBackquote(s) {
quoted = "`" + s + "`"
} else {
- quoted = strconv.Quote(s)
+ if f.plus {
+ quoted = strconv.QuoteToASCII(s)
+ } else {
+ quoted = strconv.Quote(s)
+ }
}
f.padString(quoted)
}
@@ -299,7 +324,12 @@
// fmt_qc formats the integer as a single-quoted, escaped Go character constant.
// If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmt_qc(c int64) {
- quoted := strconv.QuoteRune(int(c))
+ var quoted string
+ if f.plus {
+ quoted = strconv.QuoteRuneToASCII(int(c))
+ } else {
+ quoted = strconv.QuoteRune(int(c))
+ }
f.padString(quoted)
}
diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go
index c18a8ea..2b2a719 100644
--- a/src/pkg/fmt/print.go
+++ b/src/pkg/fmt/print.go
@@ -363,6 +363,8 @@
// temporarily turning on the unicode flag and tweaking the precision.
func (p *pp) fmtUnicode(v int64) {
precPresent := p.fmt.precPresent
+ sharp := p.fmt.sharp
+ p.fmt.sharp = false
prec := p.fmt.prec
if !precPresent {
// If prec is already set, leave it alone; otherwise 4 is minimum.
@@ -370,10 +372,13 @@
p.fmt.precPresent = true
}
p.fmt.unicode = true // turn on U+
+ p.fmt.uniQuote = sharp
p.fmt.integer(int64(v), 16, unsigned, udigits)
p.fmt.unicode = false
+ p.fmt.uniQuote = false
p.fmt.prec = prec
p.fmt.precPresent = precPresent
+ p.fmt.sharp = sharp
}
func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {