class 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не определён в destination_encoding. Поведение по умолчанию. - :undef => :replace
-
Заменять неопределённый символ в
destination_encodingстрокой замены. - :replace => string
-
Указывает строку замены. Если она не задана, для кодировок Unicode используется «uFFFD», а для остальных — «?».
- :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. Эту форму можно использовать как PCDATA в HTML 4.0.
-
‘&’ -> ‘&’
-
‘<’ -> ‘<’
-
‘>’ -> ‘>’
-
символы, не определённые в
destination_encoding-> шестнадцатеричная символьная ссылка, например &#xHH;
-
- :xml => :attr
-
Экранировать как XML AttValue. Преобразованный результат заключается в кавычки «…». Эту форму можно использовать как значение атрибута в HTML 4.0.
-
‘&’ -> ‘&’
-
‘<’ -> ‘<’
-
‘>’ -> ‘>’
-
‘“’ -> ‘"’
-
символы, не определённые в
destination_encoding-> шестнадцатеричная символьная ссылка, например &#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.
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;
} Возвращает путь преобразования 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_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;
} Вставляет string в преобразователь кодировки. Строка будет преобразована в кодировку назначения и выведена при последующих преобразованиях.
Если кодировка назначения является кодировкой с состоянием, string преобразуется с учётом текущего состояния, которое затем обновляется.
Этот метод следует использовать только при возникновении ошибки преобразования.
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)) {
output_bytesize = rb_str_capacity(output);
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 не ограничен. После преобразования размер destination_buffer устанавливается равным destination_byteoffset + фактическому числу созданных байтов. Кодировка destination_buffer также устанавливается равной destination_encoding.
primitive_convert удаляет преобразованную часть source_buffer. Удалённая часть преобразуется в destination_buffer или буферизуется в объекте Encoding::Converter.
primitive_convert останавливает преобразование при выполнении одного из следующих условий.
-
в исходном буфере обнаружена недопустимая последовательность байтов (:invalid_byte_sequence); методы
primitive_errinfoиlast_errorвозвращают подробную информацию об ошибке. -
исходный буфер неожиданно закончился (:incomplete_input); это происходит только если :partial_input не задан. Методы
primitive_errinfoиlast_errorвозвращают подробную информацию об ошибке. -
символ не может быть представлен в выходной кодировке (:undefined_conversion); методы
primitive_errinfoиlast_errorвозвращают подробную информацию об ошибке. -
после создания части выходных данных, но до завершения обработки входных данных (:after_output); это происходит только если задан :after_output.
-
буфер назначения заполнен (:destination_buffer_full); это происходит только если destination_bytesize не равен nil.
-
исходный буфер пуст (: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]
result — результат последнего вызова primitive_convert.
Остальные элементы имеют значение только в том случае, если result равен :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. При такой ошибке некоторые байты отбрасываются, а некоторые буферизуются для последующего преобразования. Последние можно вернуть. Их можно получить с помощью 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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.