String#dump
VALUE
rb_str_dump(VALUE str)
{
int encidx = rb_enc_get_index(str);
rb_encoding *enc = rb_enc_from_index(encidx);
long len;
const char *p, *pend;
char *q, *qend;
VALUE result;
int u8 = (encidx == rb_utf8_encindex());
static const char nonascii_suffix[] = ".dup.force_encoding(\"%s\")";
len = 2; /* "" */
if (!rb_enc_asciicompat(enc)) {
len += strlen(nonascii_suffix) - rb_strlen_lit("%s");
len += strlen(enc->name);
}
p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
while (p < pend) {
int clen;
unsigned char c = *p++;
switch (c) {
case '"': case '\\':
case '\n': case '\r':
case '\t': case '\f':
case '\013': case '\010': case '\007': case '\033':
clen = 2;
break;
case '#':
clen = IS_EVSTR(p, pend) ? 2 : 1;
break;
default:
if (ISPRINT(c)) {
clen = 1;
}
else {
if (u8 && c > 0x7F) { /* \u notation */
int n = rb_enc_precise_mbclen(p-1, pend, enc);
if (MBCLEN_CHARFOUND_P(n)) {
unsigned int cc = rb_enc_mbc_to_codepoint(p-1, pend, enc);
if (cc <= 0xFFFF)
clen = 6; /* \uXXXX */
else if (cc <= 0xFFFFF)
clen = 9; /* \u{XXXXX} */
else
clen = 10; /* \u{XXXXXX} */
p += MBCLEN_CHARFOUND_LEN(n)-1;
break;
}
}
clen = 4; /* \xNN */
}
break;
}
if (clen > LONG_MAX - len) {
rb_raise(rb_eRuntimeError, "string size too big");
}
len += clen;
}
result = rb_str_new(0, len);
p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
q = RSTRING_PTR(result); qend = q + len + 1;
*q++ = '"';
while (p < pend) {
unsigned char c = *p++;
if (c == '"' || c == '\\') {
*q++ = '\\';
*q++ = c;
}
else if (c == '#') {
if (IS_EVSTR(p, pend)) *q++ = '\\';
*q++ = '#';
}
else if (c == '\n') {
*q++ = '\\';
*q++ = 'n';
}
else if (c == '\r') {
*q++ = '\\';
*q++ = 'r';
}
else if (c == '\t') {
*q++ = '\\';
*q++ = 't';
}
else if (c == '\f') {
*q++ = '\\';
*q++ = 'f';
}
else if (c == '\013') {
*q++ = '\\';
*q++ = 'v';
}
else if (c == '\010') {
*q++ = '\\';
*q++ = 'b';
}
else if (c == '\007') {
*q++ = '\\';
*q++ = 'a';
}
else if (c == '\033') {
*q++ = '\\';
*q++ = 'e';
}
else if (ISPRINT(c)) {
*q++ = c;
}
else {
*q++ = '\\';
if (u8) {
int n = rb_enc_precise_mbclen(p-1, pend, enc) - 1;
if (MBCLEN_CHARFOUND_P(n)) {
int cc = rb_enc_mbc_to_codepoint(p-1, pend, enc);
p += n;
if (cc <= 0xFFFF)
snprintf(q, qend-q, "u%04X", cc); /* \uXXXX */
else
snprintf(q, qend-q, "u{%X}", cc); /* \u{XXXXX} or \u{XXXXXX} */
q += strlen(q);
continue;
}
}
snprintf(q, qend-q, "x%02X", c);
q += 3;
}
}
*q++ = '"';
*q = '\0';
if (!rb_enc_asciicompat(enc)) {
snprintf(q, qend-q, nonascii_suffix, enc->name);
encidx = rb_ascii8bit_encindex();
}
/* result from dump is ASCII */
rb_enc_associate_index(result, encidx);
ENC_CODERANGE_SET(result, ENC_CODERANGE_7BIT);
return result;
}
For an ordinary string, this method, +String#dump+, returns a printable ASCII-only version of self, enclosed in double-quotes.
For a dumped string, method String#undump is the inverse of +String#dump+; it returns a “restored” version of self, where all the dumping changes have been undone.
In the simplest case, the dumped string contains the original string, enclosed in double-quotes; this example is done in irb (interactive Ruby), which uses method inspect to render the results:
s = 'hello' # => "hello" s.dump # => "\"hello\"" s.dump.undump # => "hello"
Keep in mind that in the second line above:
-
The outer double-quotes are put on by
inspect, and are not part of the output ofdump. -
The inner double-quotes are part of the output of
dump, and are escaped byinspectbecause they are within the outer double-quotes.
To avoid confusion, we’ll use this helper method to omit the outer double-quotes:
def dump(s) print "String: ", s, "\n" print "Dumped: ", s.dump, "\n" print "Undumped: ", s.dump.undump, "\n" end
So that for string 'hello', we’ll see:
String: hello Dumped: "hello" Undumped: hello
In a dump, certain special characters are escaped:
String: " Dumped: "\"" Undumped: " String: \ Dumped: "\\" Undumped: \
In a dump, unprintable characters are replaced by printable ones; the unprintable characters are the whitespace characters (other than space itself); here we see the ordinals for those characers, together with explanatory text:
h = { 7 => 'Alert (BEL)', 8 => 'Backspace (BS)', 9 => 'Horizontal tab (HT)', 10 => 'Linefeed (LF)', 11 => 'Vertical tab (VT)', 12 => 'Formfeed (FF)', 13 => 'Carriage return (CR)' }
In this example, the dumped output is printed by method inspect, and so contains both outer double-quotes and escaped inner double-quotes:
s = '' h.keys.each {|i| s << i } # => [7, 8, 9, 10, 11, 12, 13] s # => "\a\b\t\n\v\f\r" s.dump # => "\"\\a\\b\\t\\n\\v\\f\\r\""
If self is encoded in UTF-8 and contains Unicode characters, each Unicode character is dumped as a Unicode escape sequence:
String: тест Dumped: "\u0442\u0435\u0441\u0442" Undumped: тест String: こんにちは Dumped: "\u3053\u3093\u306B\u3061\u306F" Undumped: こんにちは
If the encoding of self is not ASCII-compatible (i.e., if self.encoding.ascii_compatible? returns false), each ASCII-compatible byte is dumped as an ASCII character, and all other bytes are dumped as hexadecimal; also appends .dup.force_encoding(\"encoding\"), where <encoding> is self.encoding.name:
String: hello
Dumped: "\xFE\xFF\x00h\x00e\x00l\x00l\x00o".dup.force_encoding("UTF-16")
Undumped: hello
String: тест
Dumped: "\xFE\xFF\x04B\x045\x04A\x04B".dup.force_encoding("UTF-16")
Undumped: тест
String: こんにちは
Dumped: "\xFE\xFF0S0\x930k0a0o".dup.force_encoding("UTF-16")
Undumped: こんにちは
Ruby version changes
-
3.0
-
The following methods now return or yield
Stringinstances instead of subclass instances when called on subclass instances: [Bug #10845]
-