Spec-Zone.ru › Ruby 4.0
  1. Encoding::
  2. Converter

class Encoding::Converter

Родитель:
Object

Класс преобразования 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

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

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;
    VALUE enc = Qnil;

    enc_arg(&arg, &arg_name, &arg_enc);
    result_name = rb_econv_asciicompat_encoding(arg_name);
    if (result_name) {
        result_enc = make_encoding(result_name);
        enc = rb_enc_from_encoding(result_enc);
    }
    return 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;
}

возможные элементы параметра options:

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.

  • ‘&’ -> ‘&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 or 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 Показать исходный код
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>,

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 → exception or 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) → symbol Показать исходный код
primitive_convert(source_buffer, destination_buffer, destination_byteoffset) → symbol
primitive_convert(source_buffer, destination_buffer, destination_byteoffset, destination_bytesize) → symbol
primitive_convert(source_buffer, destination_buffer, destination_byteoffset, destination_bytesize, opt) → symbol
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 останавливает преобразование при выполнении одного из следующих условий.

  • в исходном буфере обнаружена недопустимая последовательность байтов (: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"]
primitive_errinfo → array Показать исходный код
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
#=> ""
putback → string Показать исходный код
putback(max_numbytes) → string
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
replacement → string Показать исходный код
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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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