Thanks for bothering. My questions were not related to the surrogate characters, but more general (at least I think so). Indeed, I already had read all the documents you mentioned, but obviously, that has been too long ago. So, I am glad about your answer for question 1, but I still have problems with question 2. What is that SUBCHAR? I tried to do something like
perl -e 'use warnings; use Encode; print SUBCHAR;'
but that did not work. I got warnings in every combination I could think of, e.g. Encode::SUBCHAR
$SUBCHAR
and so on. Could somebody please explain what that SUBCHAR actually is by default, how to print its current value and perhaps how to change it? Thank you very much, Nocturnus | [reply] [d/l] [select] |
The Encode documentation has it all, but it is a little bit hard to understand. It seems to be written by someone who deeply knows the implementation, which makes it hard to explain the interface.
I looked at the source, no SUBCHAR in Encode.pm, but 13 hits in Encode.xs:
(Ignore uppper and lower case when comparing with the documentation, and don't get confused by the different prefixes for some constants. Just look for names you know from the documentation. And despite the file is named Encode.xs, this ist just C code with some precompiler macros.)
/* encoding */
//// ...
if (check & (ENCODE_PERLQQ|ENCODE_HTMLCREF|ENCODE_XMLCREF)){
SV* subchar =
(fallback_cb != &PL_sv_undef)
? do_fallback_cb(aTHX_ ch, fallback_cb)
: newSVpvf(check & ENCODE_PERLQQ ? "\\x{%04"UVxf"}" :
check & ENCODE_HTMLCREF ? "&#%" UVuf ";" :
"&#x%" UVxf ";", (UV)ch);
SvUTF8_off(subchar); /* make sure no decoded string gets in */
sdone += slen + clen;
ddone += dlen + SvCUR(subchar);
sv_catsv(dst, subchar);
SvREFCNT_dec(subchar);
} else {
/* fallback char */
sdone += slen + clen;
ddone += dlen + enc->replen;
sv_catpvn(dst, (char*)enc->rep, enc->replen);
}
and
/* decoding */
//// ...
if (check &
(ENCODE_PERLQQ|ENCODE_HTMLCREF|ENCODE_XMLCREF)){
SV* subchar =
(fallback_cb != &PL_sv_undef)
? do_fallback_cb(aTHX_ (UV)s[slen], fallback_cb)
: newSVpvf("\\x%02" UVXf, (UV)s[slen]);
sdone += slen + 1;
ddone += dlen + SvCUR(subchar);
sv_catsv(dst, subchar);
SvREFCNT_dec(subchar);
} else {
sdone += slen + 1;
ddone += dlen + strlen(FBCHAR_UTF8);
sv_catpv(dst, FBCHAR_UTF8);
}
and
malformed:
//// ...
if (check & (ENCODE_PERLQQ|ENCODE_HTMLCREF|ENCODE_XMLCREF)){
SV* subchar =
(fallback_cb != &PL_sv_undef)
? do_fallback_cb(aTHX_ uv, fallback_cb)
: newSVpvf(check & ENCODE_PERLQQ
? (ulen == 1 ? "\\x%02" UVXf : "\\x{%04" UVXf "}")
: check & ENCODE_HTMLCREF ? "&#%" UVuf ";"
: "&#x%" UVxf ";", uv);
if (encode){
SvUTF8_off(subchar); /* make sure no decoded string gets in */
}
sv_catsv(dst, subchar);
SvREFCNT_dec(subchar);
} else {
sv_catpv(dst, FBCHAR_UTF8);
}
I didn't attempt to fully understand what this code does. But it is quite obvious that the various constants for CHECK (FB_PERLQQ, FB_HTMLCREF, FB_XMLCREF in perl, the same with an ENCODE_ prefix instead of FB_ in XS) select how a malformed character is replaced. SUBCHAR is an unfortunate name, it is a substitute FOR a character, not A substitute character. In fact, it is a string, existing only as a local variable in XS. You can't access it from Perl.
But there is another hint: fallback_cb, a callback function, called whenever a substitute for a malformed character is needed. This is what happens when CHECK is a code reference. Read coderef for CHECK:
coderef for CHECK
As of Encode 2.12, CHECK can also be a code reference which takes the ordinal value of the unmapped character as an argument and returns octets that represent the fallback character.
...
Even the fallback for decode must return octets, which are then decoded with the character encoding that decode accepts.
"Octets" are just what everyone else (except for the french) calls bytes. Encode uses the name "byte" for something different, "A character in the range 0..255; a special case of a Perl character." C people would call that a char. The callback must always return a string of bytes, as shown in the examples not cited here.
So to replace malformed characters with "???", just use sub { '???' } as value for CHECK. To replace them with their decimal ordinal value between @ signs, use sub { sprintf '@%d@',shift }.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
Thanks again for your in-depth answer and the time you have put into the problem. I am somehow embarrassed that I didn't look into the source code myself. My excuse is that I was convinced that somebody out there already had a list showing which value SUBCHAR is under which circumstances.
Regarding using a coderef for CHECK, I think I had got that in the meantime, but IMHO the question remains if a malformed character has an ordinal value at all (quite sure yes, but which?). Since the respective character is malformed, it probably does not have a Unicode code point, so I am unsure about what value shift would return in that case.
In the meantime, I have decided that in my case it is best to use FB_QUIET for CHECK, so examining more deeply is not what I am planning to do. Maybe later ...
I have asked question 2) mainly because I felt that not explaining SUBCHAR is a substantial lack of documentation and was hoping that I had overlooked something.
Thank you very much again!
| [reply] |