класс Encoding::Converter
Encoding класс преобразования.
Константы
- AFTER_OUTPUT
-
Остановить преобразование после завершения некоторого вывода, но до того, как весь вход был обработан. См.
primitive_convertдля примера. - CRLF_NEWLINE_DECORATOR
-
Декоратор для преобразования LF в CRLF
- CR_NEWLINE_DECORATOR
-
Декоратор для преобразования LF в CR
- INVALID_MASK
-
Маска для некорректных последовательностей байтов
- INVALID_REPLACE
-
Замена некорректных последовательностей байтов
- LF_NEWLINE_DECORATOR
-
Декоратор для преобразования CRLF и CR в LF при записи
- PARTIAL_INPUT
-
Указывает, что источник может быть частью большей строки. См.
primitive_convertдля примера. - UNDEF_HEX_CHARREF
-
Замена последовательностей байтов, которые не определены в кодировке назначения, на шестнадцатеричную ссылку на символ XML. Это допустимо для преобразования в XML.
- UNDEF_MASK
-
Маска для допустимого символа в кодировке источника, но без соответствующего(их) символа(ов) в кодировке назначения.
- UNDEF_REPLACE
-
Замена последовательностей байтов, которые не определены в кодировке назначения.
- UNIVERSAL_NEWLINE_DECORATOR
-
Декоратор для преобразования CRLF и CR в LF
- XML_ATTR_CONTENT_DECORATOR
-
Экранирование как XML AttValue
- XML_ATTR_QUOTE_DECORATOR
-
Экранирование как XML AttValue
- XML_TEXT_DECORATOR
-
Экранирование как XML CharData
Публичные методы класса
static VALUE
econv_s_asciicompat_encoding(VALUE klass, VALUE arg)
{
const char *arg_name, *result_name;
rb_encoding *arg_enc, *result_enc;
enc_arg(&arg, &arg_name, &arg_enc);
result_name = rb_econv_asciicompat_encoding(arg_name);
if (result_name == NULL)
return Qnil;
result_enc = make_encoding(result_name);
return rb_enc_from_encoding(result_enc);
} Возвращает соответствующую кодировку, совместимую с ASCII.
Возвращает nil, если аргумент — кодировка, совместимая с ASCII.
«Соответствующая кодировка, совместимая с ASCII», — это кодировка, совместимая с ASCII, которая может точно представлять те же символы, что и заданная кодировка, несовместимая с ASCII. Таким образом, при преобразовании между двумя кодировками не возникает ошибок неопределённого преобразования.
Encoding::Converter.asciicompat_encoding("ISO-2022-JP") #=> #<Encoding:stateless-ISO-2022-JP>
Encoding::Converter.asciicompat_encoding("UTF-16BE") #=> #<Encoding:UTF-8>
Encoding::Converter.asciicompat_encoding("UTF-8") #=> nil
static VALUE
econv_init(int argc, VALUE *argv, VALUE self)
{
VALUE ecopts;
VALUE snamev, dnamev;
const char *sname, *dname;
rb_encoding *senc, *denc;
rb_econv_t *ec;
int ecflags;
VALUE convpath;
if (rb_check_typeddata(self, &econv_data_type)) {
rb_raise(rb_eTypeError, "already initialized");
}
if (argc == 1 && !NIL_P(convpath = rb_check_array_type(argv[0]))) {
ec = rb_econv_init_by_convpath(self, convpath, &sname, &dname, &senc, &denc);
ecflags = 0;
ecopts = Qnil;
}
else {
econv_args(argc, argv, &snamev, &dnamev, &sname, &dname, &senc, &denc, &ecflags, &ecopts);
ec = rb_econv_open_opts(sname, dname, ecflags, ecopts);
}
if (!ec) {
VALUE exc = rb_econv_open_exc(sname, dname, ecflags);
RB_GC_GUARD(snamev);
RB_GC_GUARD(dnamev);
rb_exc_raise(exc);
}
if (!DECORATOR_P(sname, dname)) {
if (!senc)
senc = make_dummy_encoding(sname);
if (!denc)
denc = make_dummy_encoding(dname);
RB_GC_GUARD(snamev);
RB_GC_GUARD(dnamev);
}
ec->source_encoding = senc;
ec->destination_encoding = denc;
DATA_PTR(self) = ec;
return self;
} Возможные элементы параметров:
hash form:
:invalid => nil # raise error on invalid byte sequence (default)
:invalid => :replace # replace invalid byte sequence
:undef => nil # raise error on undefined conversion (default)
:undef => :replace # replace undefined conversion
:replace => string # replacement string ("?" or "\uFFFD" if not specified)
:newline => :universal # decorator for converting CRLF and CR to LF
:newline => :lf # decorator for converting CRLF and CR to LF when writing
:newline => :crlf # decorator for converting LF to CRLF
:newline => :cr # decorator for converting LF to CR
:universal_newline => true # decorator for converting CRLF and CR to LF
:crlf_newline => true # decorator for converting LF to CRLF
:cr_newline => true # decorator for converting LF to CR
:lf_newline => true # decorator for converting CRLF and CR to LF when writing
:xml => :text # escape as XML CharData.
:xml => :attr # escape as XML AttValue
integer form:
Encoding::Converter::INVALID_REPLACE
Encoding::Converter::UNDEF_REPLACE
Encoding::Converter::UNDEF_HEX_CHARREF
Encoding::Converter::UNIVERSAL_NEWLINE_DECORATOR
Encoding::Converter::LF_NEWLINE_DECORATOR
Encoding::Converter::CRLF_NEWLINE_DECORATOR
Encoding::Converter::CR_NEWLINE_DECORATOR
Encoding::Converter::XML_TEXT_DECORATOR
Encoding::Converter::XML_ATTR_CONTENT_DECORATOR
Encoding::Converter::XML_ATTR_QUOTE_DECORATOR Encoding::Converter.new создает экземпляр Encoding::Converter.
source_encoding и destination_encoding должны быть строкой или объектом Encoding.
opt должен быть nil, хэш или целым числом.
convpath должна быть массивом. convpath может содержать
-
двухэлементные массивы, которые содержат кодировки или имена кодировок, или
-
строки, представляющие имена декораторов.
Encoding::Converter.new необязательно принимает опцию. Опция должна быть хэш или целое число. Хэш опций может содержать :invalid => nil и т.д. Целое число опций должно быть логическим ИЛИ констант, таких как Encoding::Converter::INVALID_REPLACE и т.д.
- :invalid => nil
-
Вызвать ошибку при обнаружении некорректной последовательности байтов. Это стандартное поведение.
- :invalid => :replace
-
Заменить некорректную последовательность байтов строкой замены.
- :undef => nil
-
Вызвать ошибку, если символ в
source_encodingне определён в кодировке назначения. Это стандартное поведение. - :undef => :replace
-
Заменить неопределённый символ в
destination_encodingстрокой замены. - :replace => string
-
Укажите строку замены. Если не указано, используется “uFFFD” для кодировок Unicode и “?” для других.
- :universal_newline => true
-
Преобразовать CRLF и CR в LF.
- :crlf_newline => true
-
Преобразовать LF в CRLF.
- :cr_newline => true
-
Преобразовать LF в CR.
- :lf_newline => true
-
Преобразовать CRLF и CR в LF (при записи).
- :xml => :text
-
Экранировать как XML CharData. Этот формат может быть использован как HTML 4.0 PCDATA.
-
‘&’ -> ‘&’
-
‘<’ -> ‘<’
-
‘>’ -> ‘>’
-
неопределённые символы в
destination_encoding-> шестнадцатеричная CharRef, например, &#xHH;
-
- :xml => :attr
-
Экранировать как XML AttValue. Результат преобразования заключён в кавычки “…”. Этот формат можно использовать как значение атрибута HTML 4.0.
-
‘&’ -> ‘&’
-
‘<’ -> ‘<’
-
‘>’ -> ‘>’
-
‘“’ -> ‘"’
-
неопределённые символы в
destination_encoding-> шестнадцатеричная CharRef, например, &#xHH;
-
Примеры:
# UTF-16BE to UTF-8
ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
# Usually, decorators such as newline conversion are inserted last.
ec = Encoding::Converter.new("UTF-16BE", "UTF-8", :universal_newline => true)
p ec.convpath #=> [[#<Encoding:UTF-16BE>, #<Encoding:UTF-8>],
# "universal_newline"]
# But, if the last encoding is ASCII incompatible,
# decorators are inserted before the last conversion.
ec = Encoding::Converter.new("UTF-8", "UTF-16BE", :crlf_newline => true)
p ec.convpath #=> ["crlf_newline",
# [#<Encoding:UTF-8>, #<Encoding:UTF-16BE>]]
# Conversion path can be specified directly.
ec = Encoding::Converter.new(["universal_newline", ["EUC-JP", "UTF-8"], ["UTF-8", "UTF-16BE"]])
p ec.convpath #=> ["universal_newline",
# [#<Encoding:EUC-JP>, #<Encoding:UTF-8>],
# [#<Encoding:UTF-8>, #<Encoding:UTF-16BE>]]
static VALUE
econv_s_search_convpath(int argc, VALUE *argv, VALUE klass)
{
VALUE snamev, dnamev;
const char *sname, *dname;
rb_encoding *senc, *denc;
int ecflags;
VALUE ecopts;
VALUE convpath;
econv_args(argc, argv, &snamev, &dnamev, &sname, &dname, &senc, &denc, &ecflags, &ecopts);
convpath = Qnil;
transcode_search_path(sname, dname, search_convpath_i, &convpath);
if (NIL_P(convpath)) {
VALUE exc = rb_econv_open_exc(sname, dname, ecflags);
RB_GC_GUARD(snamev);
RB_GC_GUARD(dnamev);
rb_exc_raise(exc);
}
if (decorate_convpath(convpath, ecflags) == -1) {
VALUE exc = rb_econv_open_exc(sname, dname, ecflags);
RB_GC_GUARD(snamev);
RB_GC_GUARD(dnamev);
rb_exc_raise(exc);
}
return convpath;
} Возвращает путь преобразования.
p Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP")
#=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
# [#<Encoding:UTF-8>, #<Encoding:EUC-JP>]]
p Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP", universal_newline: true)
or
p Encoding::Converter.search_convpath("ISO-8859-1", "EUC-JP", newline: :universal)
#=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
# [#<Encoding:UTF-8>, #<Encoding:EUC-JP>],
# "universal_newline"]
p Encoding::Converter.search_convpath("ISO-8859-1", "UTF-32BE", universal_newline: true)
or
p Encoding::Converter.search_convpath("ISO-8859-1", "UTF-32BE", newline: :universal)
#=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
# "universal_newline",
# [#<Encoding:UTF-8>, #<Encoding:UTF-32BE>]] Методы открытого экземпляра
static VALUE
econv_equal(VALUE self, VALUE other)
{
rb_econv_t *ec1 = check_econv(self);
rb_econv_t *ec2;
int i;
if (!rb_typeddata_is_kind_of(other, &econv_data_type)) {
return Qnil;
}
ec2 = DATA_PTR(other);
if (!ec2) return Qfalse;
if (ec1->source_encoding_name != ec2->source_encoding_name &&
strcmp(ec1->source_encoding_name, ec2->source_encoding_name))
return Qfalse;
if (ec1->destination_encoding_name != ec2->destination_encoding_name &&
strcmp(ec1->destination_encoding_name, ec2->destination_encoding_name))
return Qfalse;
if (ec1->flags != ec2->flags) return Qfalse;
if (ec1->replacement_enc != ec2->replacement_enc &&
strcmp(ec1->replacement_enc, ec2->replacement_enc))
return Qfalse;
if (ec1->replacement_len != ec2->replacement_len) return Qfalse;
if (ec1->replacement_str != ec2->replacement_str &&
memcmp(ec1->replacement_str, ec2->replacement_str, ec2->replacement_len))
return Qfalse;
if (ec1->num_trans != ec2->num_trans) return Qfalse;
for (i = 0; i < ec1->num_trans; i++) {
if (ec1->elems[i].tc->transcoder != ec2->elems[i].tc->transcoder)
return Qfalse;
}
return Qtrue;
} static VALUE
econv_convert(VALUE self, VALUE source_string)
{
VALUE ret, dst;
VALUE av[5];
int ac;
rb_econv_t *ec = check_econv(self);
StringValue(source_string);
dst = rb_str_new(NULL, 0);
av[0] = rb_str_dup(source_string);
av[1] = dst;
av[2] = Qnil;
av[3] = Qnil;
av[4] = INT2NUM(ECONV_PARTIAL_INPUT);
ac = 5;
ret = econv_primitive_convert(ac, av, self);
if (ret == sym_invalid_byte_sequence ||
ret == sym_undefined_conversion ||
ret == sym_incomplete_input) {
VALUE exc = make_econv_exception(ec);
rb_exc_raise(exc);
}
if (ret == sym_finished) {
rb_raise(rb_eArgError, "converter already finished");
}
if (ret != sym_source_buffer_empty) {
rb_bug("unexpected result of econv_primitive_convert");
}
return dst;
} Преобразует source_string и возвращает destination_string.
source_string предполагается как часть исходных данных. т.е. :partial_input=>true задается внутри. Метод finish следует использовать последним.
ec = Encoding::Converter.new("utf-8", "euc-jp")
puts ec.convert("\u3042").dump #=> "\xA4\xA2"
puts ec.finish.dump #=> ""
ec = Encoding::Converter.new("euc-jp", "utf-8")
puts ec.convert("\xA4").dump #=> ""
puts ec.convert("\xA2").dump #=> "\xE3\x81\x82"
puts ec.finish.dump #=> ""
ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
puts ec.convert("\xE3").dump #=> "".force_encoding("ISO-2022-JP")
puts ec.convert("\x81").dump #=> "".force_encoding("ISO-2022-JP")
puts ec.convert("\x82").dump #=> "\e$B$\"".force_encoding("ISO-2022-JP")
puts ec.finish.dump #=> "\e(B".force_encoding("ISO-2022-JP")
Если произошла ошибка преобразования, генерируется Encoding::UndefinedConversionError или Encoding::InvalidByteSequenceError. Encoding::Converter#convert не предоставляет методов для восстановления или перезапуска после этих исключений. Если требуется обрабатывать эти ошибки преобразования, используйте Encoding::Converter#primitive_convert.
Возвращает путь преобразования ec.
Результат — массив преобразований.
ec = Encoding::Converter.new("ISO-8859-1", "EUC-JP", crlf_newline: true)
p ec.convpath
#=> [[#<Encoding:ISO-8859-1>, #<Encoding:UTF-8>],
# [#<Encoding:UTF-8>, #<Encoding:EUC-JP>],
# "crlf_newline"]
Каждый элемент массива — пара кодировок или строка. Пара означает преобразование кодировок. Строка означает декоратор.
В приведённом выше примере, [#<Encoding:ISO-8859-1>, static VALUE
econv_convpath(VALUE self)
{
rb_econv_t *ec = check_econv(self);
VALUE result;
int i;
result = rb_ary_new();
for (i = 0; i < ec->num_trans; i++) {
const rb_transcoder *tr = ec->elems[i].tc->transcoder;
VALUE v;
if (DECORATOR_P(tr->src_encoding, tr->dst_encoding))
v = rb_str_new_cstr(tr->dst_encoding);
else
v = rb_assoc_new(make_encobj(tr->src_encoding), make_encobj(tr->dst_encoding));
rb_ary_push(result, v);
}
return result;
}
static VALUE
econv_destination_encoding(VALUE self)
{
rb_econv_t *ec = check_econv(self);
return econv_get_encoding(ec->destination_encoding);
} Возвращает целевую кодировку в виде объекта Encoding.
static VALUE
econv_finish(VALUE self)
{
VALUE ret, dst;
VALUE av[5];
int ac;
rb_econv_t *ec = check_econv(self);
dst = rb_str_new(NULL, 0);
av[0] = Qnil;
av[1] = dst;
av[2] = Qnil;
av[3] = Qnil;
av[4] = INT2FIX(0);
ac = 5;
ret = econv_primitive_convert(ac, av, self);
if (ret == sym_invalid_byte_sequence ||
ret == sym_undefined_conversion ||
ret == sym_incomplete_input) {
VALUE exc = make_econv_exception(ec);
rb_exc_raise(exc);
}
if (ret != sym_finished) {
rb_bug("unexpected result of econv_primitive_convert");
}
return dst;
} Завершает преобразование. Возвращает последнюю часть преобразованной строки.
ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
p ec.convert("\u3042") #=> "\e$B$\""
p ec.finish #=> "\e(B"
static VALUE
econv_insert_output(VALUE self, VALUE string)
{
const char *insert_enc;
int ret;
rb_econv_t *ec = check_econv(self);
StringValue(string);
insert_enc = rb_econv_encoding_to_insert_output(ec);
string = rb_str_encode(string, rb_enc_from_encoding(rb_enc_find(insert_enc)), 0, Qnil);
ret = rb_econv_insert_output(ec, (const unsigned char *)RSTRING_PTR(string), RSTRING_LEN(string), insert_enc);
if (ret == -1) {
rb_raise(rb_eArgError, "too big string");
}
return Qnil;
} Вставляет строку в преобразователь кодировок. Строка будет преобразована в целевую кодировку и выведена при последующих преобразованиях.
Если целевая кодировка имеет состояние, строка преобразуется в соответствии с состоянием, и состояние обновляется.
Этот метод следует использовать только при возникновении ошибки преобразования.
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
src = "HIRAGANA LETTER A is \u{3042}."
dst = ""
p ec.primitive_convert(src, dst) #=> :undefined_conversion
puts "[#{dst.dump}, #{src.dump}]" #=> ["HIRAGANA LETTER A is ", "."]
ec.insert_output("<err>")
p ec.primitive_convert(src, dst) #=> :finished
puts "[#{dst.dump}, #{src.dump}]" #=> ["HIRAGANA LETTER A is <err>.", ""]
ec = Encoding::Converter.new("utf-8", "iso-2022-jp")
src = "\u{306F 3041 3068 2661 3002}" # U+2661 is not representable in iso-2022-jp
dst = ""
p ec.primitive_convert(src, dst) #=> :undefined_conversion
puts "[#{dst.dump}, #{src.dump}]" #=> ["\e$B$O$!$H".force_encoding("ISO-2022-JP"), "\xE3\x80\x82"]
ec.insert_output "?" # state change required to output "?".
p ec.primitive_convert(src, dst) #=> :finished
puts "[#{dst.dump}, #{src.dump}]" #=> ["\e$B$O$!$H\e(B?\e$B!#\e(B".force_encoding("ISO-2022-JP"), ""]
static VALUE
econv_inspect(VALUE self)
{
const char *cname = rb_obj_classname(self);
rb_econv_t *ec;
TypedData_Get_Struct(self, rb_econv_t, &econv_data_type, ec);
if (!ec)
return rb_sprintf("#<%s: uninitialized>", cname);
else {
const char *sname = ec->source_encoding_name;
const char *dname = ec->destination_encoding_name;
VALUE str;
str = rb_sprintf("#<%s: ", cname);
econv_description(sname, dname, ec->flags, str);
rb_str_cat2(str, ">");
return str;
}
} Возвращает удобочитаемый вид объекта ec
ec = Encoding::Converter.new("iso-8859-1", "utf-8")
puts ec.inspect #=> #<Encoding::Converter: ISO-8859-1 to UTF-8>
static VALUE
econv_last_error(VALUE self)
{
rb_econv_t *ec = check_econv(self);
VALUE exc;
exc = make_econv_exception(ec);
if (NIL_P(exc))
return Qnil;
return exc;
} Возвращает объект исключения для последнего преобразования. Возвращает nil, если последнее преобразование не вызвало ошибки.
«Ошибка» означает, что Encoding::InvalidByteSequenceError и Encoding::UndefinedConversionError для Encoding::Converter#convert и :invalid_byte_sequence, :incomplete_input и :undefined_conversion для Encoding::Converter#primitive_convert.
ec = Encoding::Converter.new("utf-8", "iso-8859-1")
p ec.primitive_convert(src="\xf1abcd", dst="") #=> :invalid_byte_sequence
p ec.last_error #=> #<Encoding::InvalidByteSequenceError: "\xF1" followed by "a" on UTF-8>
p ec.primitive_convert(src, dst, nil, 1) #=> :destination_buffer_full
p ec.last_error #=> nil
static VALUE
econv_primitive_convert(int argc, VALUE *argv, VALUE self)
{
VALUE input, output, output_byteoffset_v, output_bytesize_v, opt, flags_v;
rb_econv_t *ec = check_econv(self);
rb_econv_result_t res;
const unsigned char *ip, *is;
unsigned char *op, *os;
long output_byteoffset, output_bytesize;
unsigned long output_byteend;
int flags;
argc = rb_scan_args(argc, argv, "23:", &input, &output, &output_byteoffset_v, &output_bytesize_v, &flags_v, &opt);
if (NIL_P(output_byteoffset_v))
output_byteoffset = 0; /* dummy */
else
output_byteoffset = NUM2LONG(output_byteoffset_v);
if (NIL_P(output_bytesize_v))
output_bytesize = 0; /* dummy */
else
output_bytesize = NUM2LONG(output_bytesize_v);
if (!NIL_P(flags_v)) {
if (!NIL_P(opt)) {
rb_error_arity(argc + 1, 2, 5);
}
flags = NUM2INT(rb_to_int(flags_v));
}
else if (!NIL_P(opt)) {
VALUE v;
flags = 0;
v = rb_hash_aref(opt, sym_partial_input);
if (RTEST(v))
flags |= ECONV_PARTIAL_INPUT;
v = rb_hash_aref(opt, sym_after_output);
if (RTEST(v))
flags |= ECONV_AFTER_OUTPUT;
}
else {
flags = 0;
}
StringValue(output);
if (!NIL_P(input))
StringValue(input);
rb_str_modify(output);
if (NIL_P(output_bytesize_v)) {
#if USE_RVARGC
output_bytesize = rb_str_capacity(output);
#else
output_bytesize = RSTRING_EMBED_LEN_MAX;
#endif
if (!NIL_P(input) && output_bytesize < RSTRING_LEN(input))
output_bytesize = RSTRING_LEN(input);
}
retry:
if (NIL_P(output_byteoffset_v))
output_byteoffset = RSTRING_LEN(output);
if (output_byteoffset < 0)
rb_raise(rb_eArgError, "negative output_byteoffset");
if (RSTRING_LEN(output) < output_byteoffset)
rb_raise(rb_eArgError, "output_byteoffset too big");
if (output_bytesize < 0)
rb_raise(rb_eArgError, "negative output_bytesize");
output_byteend = (unsigned long)output_byteoffset +
(unsigned long)output_bytesize;
if (output_byteend < (unsigned long)output_byteoffset ||
LONG_MAX < output_byteend)
rb_raise(rb_eArgError, "output_byteoffset+output_bytesize too big");
if (rb_str_capacity(output) < output_byteend)
rb_str_resize(output, output_byteend);
if (NIL_P(input)) {
ip = is = NULL;
}
else {
ip = (const unsigned char *)RSTRING_PTR(input);
is = ip + RSTRING_LEN(input);
}
op = (unsigned char *)RSTRING_PTR(output) + output_byteoffset;
os = op + output_bytesize;
res = rb_econv_convert(ec, &ip, is, &op, os, flags);
rb_str_set_len(output, op-(unsigned char *)RSTRING_PTR(output));
if (!NIL_P(input)) {
rb_str_drop_bytes(input, ip - (unsigned char *)RSTRING_PTR(input));
}
if (NIL_P(output_bytesize_v) && res == econv_destination_buffer_full) {
if (LONG_MAX / 2 < output_bytesize)
rb_raise(rb_eArgError, "too long conversion result");
output_bytesize *= 2;
output_byteoffset_v = Qnil;
goto retry;
}
if (ec->destination_encoding) {
rb_enc_associate(output, ec->destination_encoding);
}
return econv_result_to_symbol(res);
} Возможные элементы opt:
hash form: :partial_input => true # source buffer may be part of larger source :after_output => true # stop conversion after output before input integer form: Encoding::Converter::PARTIAL_INPUT Encoding::Converter::AFTER_OUTPUT
Возможные результаты:
:invalid_byte_sequence :incomplete_input :undefined_conversion :after_output :destination_buffer_full :source_buffer_empty :finished
primitive_convert преобразует source_buffer в destination_buffer.
source_buffer должен быть строкой или nil. nil означает пустую строку.
destination_buffer должен быть строкой.
destination_byteoffset должен быть целым числом или nil. nil означает конец destination_buffer. Если опущено, подразумевается nil.
destination_bytesize должно быть целым числом или nil. nil означает неограниченное значение. Если опущено, подразумевается nil.
opt должно быть nil, хэш или целое число. nil означает отсутствие флагов. Если опущено, подразумевается nil.
primitive_convert преобразует содержимое source_buffer с начала и сохраняет результат в destination_buffer.
destination_byteoffset и destination_bytesize определяют область, в которую сохраняется преобразованный результат. destination_byteoffset указывает начальную позицию в destination_buffer в байтах. Если destination_byteoffset является nil, используется destination_buffer.bytesize для добавления результата. destination_bytesize определяет максимальное количество байтов. Если destination_bytesize является nil, размер назначения не ограничен. После преобразования destination_buffer изменяет размер на destination_byteoffset + фактическое количество произведённых байтов. Также кодировка destination_buffer устанавливается на destination_encoding.
primitive_convert отбрасывает преобразованную часть source_buffer. Отброшенная часть преобразуется в destination_buffer или буферизуется в объекте Encoding::Converter.
primitive_convert останавливает преобразование, когда выполняется одно из следующих условий.
-
Найдена некорректная последовательность байтов в буфере source (:invalid_byte_sequence)
primitive_errinfoиlast_errorметоды возвращают детали ошибки. -
Неожиданный конец буфера source (:incomplete_input) Это происходит только тогда, когда :partial_input не указано.
primitive_errinfoиlast_errorметоды возвращают детали ошибки. -
Символ не может быть представлен в кодировке вывода (:undefined_conversion)
primitive_errinfoиlast_errorметоды возвращают детали ошибки. -
После генерации некоторого вывода, перед завершением ввода (:after_output) Это происходит только при указании :after_output.
-
Буфер назначения заполнен (:destination_buffer_full) Это происходит только при non-nil значении destination_bytesize.
-
Буфер источника пуст (:source_buffer_empty) Это происходит только при указании :partial_input.
-
Преобразование завершено (:finished)
Пример:
ec = Encoding::Converter.new("UTF-8", "UTF-16BE")
ret = ec.primitive_convert(src="pi", dst="", nil, 100)
p [ret, src, dst] #=> [:finished, "", "\x00p\x00i"]
ec = Encoding::Converter.new("UTF-8", "UTF-16BE")
ret = ec.primitive_convert(src="pi", dst="", nil, 1)
p [ret, src, dst] #=> [:destination_buffer_full, "i", "\x00"]
ret = ec.primitive_convert(src, dst="", nil, 1)
p [ret, src, dst] #=> [:destination_buffer_full, "", "p"]
ret = ec.primitive_convert(src, dst="", nil, 1)
p [ret, src, dst] #=> [:destination_buffer_full, "", "\x00"]
ret = ec.primitive_convert(src, dst="", nil, 1)
p [ret, src, dst] #=> [:finished, "", "i"]
static VALUE
econv_primitive_errinfo(VALUE self)
{
rb_econv_t *ec = check_econv(self);
VALUE ary;
ary = rb_ary_new2(5);
rb_ary_store(ary, 0, econv_result_to_symbol(ec->last_error.result));
rb_ary_store(ary, 4, Qnil);
if (ec->last_error.source_encoding)
rb_ary_store(ary, 1, rb_str_new2(ec->last_error.source_encoding));
if (ec->last_error.destination_encoding)
rb_ary_store(ary, 2, rb_str_new2(ec->last_error.destination_encoding));
if (ec->last_error.error_bytes_start) {
rb_ary_store(ary, 3, rb_str_new((const char *)ec->last_error.error_bytes_start, ec->last_error.error_bytes_len));
rb_ary_store(ary, 4, rb_str_new((const char *)ec->last_error.error_bytes_start + ec->last_error.error_bytes_len, ec->last_error.readagain_len));
}
return ary;
} primitive_errinfo возвращает важную информацию об последней ошибке в виде массива из 5 элементов:
[result, enc1, enc2, error_bytes, readagain_bytes]
результат — последний результат primitive_convert.
Другие элементы значимы только при результате :invalid_byte_sequence, :incomplete_input или :undefined_conversion.
enc1 и enc2 указывают шаг преобразования как пару строк. Например, преобразователь из EUC-JP в ISO-8859-1 преобразует строку следующим образом: EUC-JP -> UTF-8 -> ISO-8859-1. Таким образом, [enc1, enc2] является либо [“EUC-JP”, “UTF-8”], либо [“UTF-8”, “ISO-8859-1”].
error_bytes и readagain_bytes указывают последовательности байтов, которые вызвали ошибку. error_bytes — отброшенная часть. readagain_bytes — буферизованная часть, которая считывается снова при следующем преобразовании.
Пример:
# \xff is invalid as EUC-JP.
ec = Encoding::Converter.new("EUC-JP", "Shift_JIS")
ec.primitive_convert(src="\xff", dst="", nil, 10)
p ec.primitive_errinfo
#=> [:invalid_byte_sequence, "EUC-JP", "Shift_JIS", "\xFF", ""]
# HIRAGANA LETTER A (\xa4\xa2 in EUC-JP) is not representable in ISO-8859-1.
# Since this error is occur in UTF-8 to ISO-8859-1 conversion,
# error_bytes is HIRAGANA LETTER A in UTF-8 (\xE3\x81\x82).
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
ec.primitive_convert(src="\xa4\xa2", dst="", nil, 10)
p ec.primitive_errinfo
#=> [:undefined_conversion, "UTF-8", "ISO-8859-1", "\xE3\x81\x82", ""]
# partial character is invalid
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
ec.primitive_convert(src="\xa4", dst="", nil, 10)
p ec.primitive_errinfo
#=> [:incomplete_input, "EUC-JP", "UTF-8", "\xA4", ""]
# Encoding::Converter::PARTIAL_INPUT prevents invalid errors by
# partial characters.
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
ec.primitive_convert(src="\xa4", dst="", nil, 10, Encoding::Converter::PARTIAL_INPUT)
p ec.primitive_errinfo
#=> [:source_buffer_empty, nil, nil, nil, nil]
# \xd8\x00\x00@ is invalid as UTF-16BE because
# no low surrogate after high surrogate (\xd8\x00).
# It is detected by 3rd byte (\00) which is part of next character.
# So the high surrogate (\xd8\x00) is discarded and
# the 3rd byte is read again later.
# Since the byte is buffered in ec, it is dropped from src.
ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
ec.primitive_convert(src="\xd8\x00\x00@", dst="", nil, 10)
p ec.primitive_errinfo
#=> [:invalid_byte_sequence, "UTF-16BE", "UTF-8", "\xD8\x00", "\x00"]
p src
#=> "@"
# Similar to UTF-16BE, \x00\xd8@\x00 is invalid as UTF-16LE.
# The problem is detected by 4th byte.
ec = Encoding::Converter.new("UTF-16LE", "UTF-8")
ec.primitive_convert(src="\x00\xd8@\x00", dst="", nil, 10)
p ec.primitive_errinfo
#=> [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "@\x00"]
p src
#=> ""
static VALUE
econv_putback(int argc, VALUE *argv, VALUE self)
{
rb_econv_t *ec = check_econv(self);
int n;
int putbackable;
VALUE str, max;
if (!rb_check_arity(argc, 0, 1) || NIL_P(max = argv[0])) {
n = rb_econv_putbackable(ec);
}
else {
n = NUM2INT(max);
putbackable = rb_econv_putbackable(ec);
if (putbackable < n)
n = putbackable;
}
str = rb_str_new(NULL, n);
rb_econv_putback(ec, (unsigned char *)RSTRING_PTR(str), n);
if (ec->source_encoding) {
rb_enc_associate(str, ec->source_encoding);
}
return str;
} Возвращает обратно байты, которые будут преобразованы.
Байты вызваны ошибкой invalid_byte_sequence. При ошибке invalid_byte_sequence некоторые байты отбрасываются, а некоторые буферизуются для последующего преобразования. Последние байты можно вернуть обратно. Это можно наблюдать в Encoding::InvalidByteSequenceError#readagain_bytes и Encoding::Converter#primitive_errinfo.
ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
src = "\x00\xd8\x61\x00"
dst = ""
p ec.primitive_convert(src, dst) #=> :invalid_byte_sequence
p ec.primitive_errinfo #=> [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
p ec.putback #=> "a\x00"
p ec.putback #=> "" # no more bytes to put back
static VALUE
econv_get_replacement(VALUE self)
{
rb_econv_t *ec = check_econv(self);
int ret;
rb_encoding *enc;
ret = make_replacement(ec);
if (ret == -1) {
rb_raise(rb_eUndefinedConversionError, "replacement character setup failed");
}
enc = rb_enc_find(ec->replacement_enc);
return rb_enc_str_new((const char *)ec->replacement_str, (long)ec->replacement_len, enc);
} Возвращает строку замены.
ec = Encoding::Converter.new("euc-jp", "us-ascii")
p ec.replacement #=> "?"
ec = Encoding::Converter.new("euc-jp", "utf-8")
p ec.replacement #=> "\uFFFD"
static VALUE
econv_set_replacement(VALUE self, VALUE arg)
{
rb_econv_t *ec = check_econv(self);
VALUE string = arg;
int ret;
rb_encoding *enc;
StringValue(string);
enc = rb_enc_get(string);
ret = rb_econv_set_replacement(ec,
(const unsigned char *)RSTRING_PTR(string),
RSTRING_LEN(string),
rb_enc_name(enc));
if (ret == -1) {
/* xxx: rb_eInvalidByteSequenceError? */
rb_raise(rb_eUndefinedConversionError, "replacement character setup failed");
}
return arg;
} Устанавливает строку замены.
ec = Encoding::Converter.new("utf-8", "us-ascii", :undef => :replace)
ec.replacement = "<undef>"
p ec.convert("a \u3042 b") #=> "a <undef> b"
static VALUE
econv_source_encoding(VALUE self)
{
rb_econv_t *ec = check_econv(self);
return econv_get_encoding(ec->source_encoding);
} Возвращает кодировку источника в виде объекта Encoding.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.