1
0
mirror of https://github.com/golang/go synced 2024-11-25 16:07:56 -07:00

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
This commit is contained in:
Rob Pike 2011-06-11 00:03:02 +00:00
parent 4b1170d2b1
commit d152fe74e1
4 changed files with 50 additions and 4 deletions

View File

@ -63,11 +63,13 @@
number of characters to output, truncating if necessary. number of characters to output, truncating if necessary.
Other flags: 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) - 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); # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
0X for hex (%#X); suppress 0x for %p (%#p); 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); ' ' (space) leave a space for elided sign in numbers (% d);
put spaces between bytes printing strings or slices in hex (% x, % X) put spaces between bytes printing strings or slices in hex (% x, % X)
0 pad with leading zeros rather than spaces 0 pad with leading zeros rather than spaces

View File

@ -133,6 +133,7 @@ var fmttests = []struct {
{"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`},
{"%q", "abc\xffdef", `"abc\xffdef"`}, {"%q", "abc\xffdef", `"abc\xffdef"`},
{"%q", "\u263a", `"☺"`}, {"%q", "\u263a", `"☺"`},
{"%+q", "\u263a", `"\u263a"`},
{"%q", "\U0010ffff", `"\U0010ffff"`}, {"%q", "\U0010ffff", `"\U0010ffff"`},
// escaped characters // escaped characters
@ -145,6 +146,8 @@ var fmttests = []struct {
{"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`}, {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`},
{"%q", '"', `'"'`}, {"%q", '"', `'"'`},
{"%q", '\'', `'\''`}, {"%q", '\'', `'\''`},
{"%q", "\u263a", `"☺"`},
{"%+q", "\u263a", `"\u263a"`},
// width // width
{"%5s", "abc", " abc"}, {"%5s", "abc", " abc"},
@ -187,6 +190,12 @@ var fmttests = []struct {
{"%U", 0x12345, "U+12345"}, {"%U", 0x12345, "U+12345"},
{"%10.6U", 0xABC, " U+000ABC"}, {"%10.6U", 0xABC, " U+000ABC"},
{"%-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 // floats
{"%+.3e", 0.0, "+0.000e+00"}, {"%+.3e", 0.0, "+0.000e+00"},

View File

@ -7,6 +7,7 @@ package fmt
import ( import (
"bytes" "bytes"
"strconv" "strconv"
"unicode"
"utf8" "utf8"
) )
@ -50,6 +51,7 @@ type fmt struct {
sharp bool sharp bool
space bool space bool
unicode bool unicode bool
uniQuote bool // Use 'x'= prefix for %U if printable.
zero bool zero bool
} }
@ -63,6 +65,7 @@ func (f *fmt) clearflags() {
f.sharp = false f.sharp = false
f.space = false f.space = false
f.unicode = false f.unicode = false
f.uniQuote = false
f.zero = false f.zero = false
} }
@ -232,6 +235,24 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
i-- i--
buf[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:]) f.pad(buf[i:])
} }
@ -291,7 +312,11 @@ func (f *fmt) fmt_q(s string) {
if f.sharp && strconv.CanBackquote(s) { if f.sharp && strconv.CanBackquote(s) {
quoted = "`" + s + "`" quoted = "`" + s + "`"
} else { } else {
quoted = strconv.Quote(s) if f.plus {
quoted = strconv.QuoteToASCII(s)
} else {
quoted = strconv.Quote(s)
}
} }
f.padString(quoted) f.padString(quoted)
} }
@ -299,7 +324,12 @@ func (f *fmt) fmt_q(s string) {
// fmt_qc formats the integer as a single-quoted, escaped Go character constant. // fmt_qc formats the integer as a single-quoted, escaped Go character constant.
// If the character is not valid Unicode, it will print '\ufffd'. // If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmt_qc(c int64) { 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) f.padString(quoted)
} }

View File

@ -363,6 +363,8 @@ func (p *pp) fmt0x64(v uint64, leading0x bool) {
// temporarily turning on the unicode flag and tweaking the precision. // temporarily turning on the unicode flag and tweaking the precision.
func (p *pp) fmtUnicode(v int64) { func (p *pp) fmtUnicode(v int64) {
precPresent := p.fmt.precPresent precPresent := p.fmt.precPresent
sharp := p.fmt.sharp
p.fmt.sharp = false
prec := p.fmt.prec prec := p.fmt.prec
if !precPresent { if !precPresent {
// If prec is already set, leave it alone; otherwise 4 is minimum. // If prec is already set, leave it alone; otherwise 4 is minimum.
@ -370,10 +372,13 @@ func (p *pp) fmtUnicode(v int64) {
p.fmt.precPresent = true p.fmt.precPresent = true
} }
p.fmt.unicode = true // turn on U+ p.fmt.unicode = true // turn on U+
p.fmt.uniQuote = sharp
p.fmt.integer(int64(v), 16, unsigned, udigits) p.fmt.integer(int64(v), 16, unsigned, udigits)
p.fmt.unicode = false p.fmt.unicode = false
p.fmt.uniQuote = false
p.fmt.prec = prec p.fmt.prec = prec
p.fmt.precPresent = precPresent p.fmt.precPresent = precPresent
p.fmt.sharp = sharp
} }
func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {