Spec-Zone.ru › Ruby 3.3

класс Encoding::Converter

Родитель:
Объект

Encoding класс преобразования.

Константы

AFTER_OUTPUT

AFTER_OUTPUT

Остановить преобразование после завершения некоторого вывода, но до того, как весь вход был обработан. См. primitive_convert для примера.

CRLF_NEWLINE_DECORATOR

CRLF_NEWLINE_DECORATOR

Декоратор для преобразования LF в CRLF

CR_NEWLINE_DECORATOR

CR_NEWLINE_DECORATOR

Декоратор для преобразования LF в CR

INVALID_MASK

INVALID_MASK

Маска для некорректных последовательностей байтов

INVALID_REPLACE

INVALID_REPLACE

Замена некорректных последовательностей байтов

LF_NEWLINE_DECORATOR

LF_NEWLINE_DECORATOR

Декоратор для преобразования CRLF и CR в LF при записи

PARTIAL_INPUT

PARTIAL_INPUT

Указывает, что источник может быть частью большей строки. См. primitive_convert для примера.

UNDEF_HEX_CHARREF

UNDEF_HEX_CHARREF

Замена последовательностей байтов, которые не определены в кодировке назначения, на шестнадцатеричную ссылку на символ XML. Это допустимо для преобразования в XML.

UNDEF_MASK

UNDEF_MASK

Маска для допустимого символа в кодировке источника, но без соответствующего(их) символа(ов) в кодировке назначения.

UNDEF_REPLACE

UNDEF_REPLACE

Замена последовательностей байтов, которые не определены в кодировке назначения.

UNIVERSAL_NEWLINE_DECORATOR

UNIVERSAL_NEWLINE_DECORATOR

Декоратор для преобразования CRLF и CR в LF

XML_ATTR_CONTENT_DECORATOR

XML_ATTR_CONTENT_DECORATOR

Экранирование как XML AttValue

XML_ATTR_QUOTE_DECORATOR

XML_ATTR_QUOTE_DECORATOR

Экранирование как XML AttValue

XML_TEXT_DECORATOR

XML_TEXT_DECORATOR

Экранирование как XML CharData

Публичные методы класса

Encoding::Converter.asciicompat_encoding(string) → encoding or nil Показать исходный код
Encoding::Converter.asciicompat_encoding(encoding) → encoding or nil
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
Encoding::Converter.new(source_encoding, destination_encoding) Показать исходный код
Encoding::Converter.new(source_encoding, destination_encoding, opt)
Encoding::Converter.new(convpath)
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.

  • ‘&’ -> ‘&amp;’

  • ‘<’ -> ‘&lt;’

  • ‘>’ -> ‘&gt;’

  • неопределённые символы в destination_encoding -> шестнадцатеричная CharRef, например, &#xHH;

:xml => :attr

Экранировать как XML AttValue. Результат преобразования заключён в кавычки “…”. Этот формат можно использовать как значение атрибута HTML 4.0.

  • ‘&’ -> ‘&amp;’

  • ‘<’ -> ‘&lt;’

  • ‘>’ -> ‘&gt;’

  • ‘“’ -> ‘&quot;’

  • неопределённые символы в 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>]]
Encoding::Converter.search_convpath(source_encoding, destination_encoding) → ary Показать исходный код
Encoding::Converter.search_convpath(source_encoding, destination_encoding, opt) → ary
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>]]

Общедоступные методы экземпляра

ec == other → true или false Показать исходный код
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;
}
convert(source_string) → destination_string Показать исходный код
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.

convpath → ary Показать исходный код

Возвращает путь преобразования 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;
}

destination_encoding → encoding Показать исходный код
static VALUE
econv_destination_encoding(VALUE self)
{
    rb_econv_t *ec = check_econv(self);
    return econv_get_encoding(ec->destination_encoding);
}

Возвращает целевую кодировку в виде объекта Encoding.

finish → string Показать исходный код
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"
insert_output(string) → nil Показать исходный код
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"), ""]
inspect → string Показать исходный код
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>
last_error → исключение или nil Показать исходный код
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
primitive_convert(source_buffer, destination_buffer) → символ
primitive_convert(source_buffer, destination_buffer, destination_byteoffset) → символ
primitive_convert(source_buffer, destination_buffer, destination_byteoffset, destination_bytesize) → символ
primitive_convert(source_buffer, destination_buffer, destination_byteoffset, destination_bytesize, opt) → символ
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_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) это происходит только когда destination_bytesize не равен nil.

  • буфер source пуст (: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"]
primitive_errinfo → массив Показать исходный код
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
#=> ""
putback → строка
putback(max_numbytes) → строка
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
replacement → строка Показать исходный код
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"
replacement = string Показать исходный код
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"
source_encoding → encoding Показать исходный код
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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API