sub escape_5_10 { join '', map { $_ > 255 ? sprintf('\\x{%04X}', $_) : chr() =~ /[[:cntrl:]]/ ? sprintf('\\x%02X', $_) : quotemeta(chr()) } unpack('W*', @_ ? $_[0] : $_) } sub escape { join '', map { ord() > 255 ? sprintf('\\x{%04X}', ord()) : /[[:cntrl:]]/ ? sprintf('\\x%02X', ord()) : quotemeta() } map /./gs, @_ ? $_[0] : $_ } sub unescape { my $s = @_ ? $_[0] : $_; $s =~ s/ \G (?: \\x \{ ([0-9a-fA-F]+) \} | \\x ([0-9a-fA-F]{1,2}) | \\(.) | # No escapes ) ([^\\]*) / ( defined($1) ? chr(hex($1)) : defined($2) ? chr(hex($2)) : defined($3) ? $3 : '' ) . $4 /xesg; $s } # XXX Assumes. Good enough. Avoids warn. binmode STDOUT, ':encoding(UTF-8)'; my $s = '<3' # \W and \w . chr(0x04D2) # wide char . "\cC"; # ctrl char print("$s\n"); $s = escape($s); print("$s\n"); $s = unescape($s); print("$s\n");