Выполните преобразование кодировки файлов с помощью Rebol 3

Я хочу использовать Rebol 3 для чтения файла на Latin1 и преобразования его в UTF-8. Есть ли встроенная функция, которую я могу использовать, или какая-то внешняя библиотека? Где я могу найти это?


person giuliolunati    schedule 12.02.2014    source источник
comment
Примечание: я думаю, что в наши дни переключение ваших входных файлов на UTF-8 - лучшая идея... если это позволяют ограничения вашей ситуации. Это легко сделать с помощью приведенного ниже метода @earl, а затем записать его обратно. Так, например, напишите %myfile.utf8 (latin1-to-utf8 read %myfile.latin1) Тогда вам не нужно заботиться о том, насколько быстро или медленно выполняется преобразование, потому что вы только один раз...   -  person HostileFork says dont trust SE    schedule 12.02.2014
comment
Полностью с тобой согласен. Все мои файлы в формате utf8. :-) Но некоторые другие нет :-(   -  person giuliolunati    schedule 12.02.2014


Ответы (4)


В Rebol есть функция invalid-utf?, которая ищет в двоичном значении байт, который не является частью допустимой последовательности UTF-8. Мы можем просто зацикливаться, пока не найдем и не заменим их все, а затем преобразовать наше двоичное значение в строку:

latin1-to-utf8: function [binary [binary!]][
    mark: :binary
    while [mark: invalid-utf? mark][
        change/part mark to char! mark/1 1
    ]
    to string! binary
]

Эта функция изменяет исходный двоичный файл. Вместо этого мы можем создать новую строку, которая оставит двоичное значение нетронутым:

latin1-to-utf8: function [binary [binary!]][
    mark: :binary
    to string! rejoin collect [
        while [mark: invalid-utf? binary][
            keep copy/part binary mark  ; keeps the portion up to the bad byte
            keep to char! mark/1        ; converts the bad byte to good bytes
            binary: next mark           ; set the series beyond the bad byte
        ]
        keep binary                     ; keep whatever is remaining
    ]
]

Бонус: вот небольшая Rebmu версия вышеприведенного — rebmu/args snippet #{DECAFBAD}, где snippet:

; modifying
IUgetLOAD"invalid-utf?"MaWT[MiuM][MisMtcTKm]tsA

; copying
IUgetLOAD"invalid-utf?"MaTSrjCT[wt[MiuA][kp copy/partAmKPtcFm AnxM]kpA]
person rgchris    schedule 05.04.2014
comment
В отличие от других предложенных вариантов, эта версия возвращает не binary!, а string! (так что технически она только декодирует латиницу 1 в Unicode, но не кодирует ее снова в UTF-8). Поэтому для истинного перекодирования Latin-1 в UTF-8 просто не используйте вызовы to string!. - person earl; 05.04.2014
comment
Намного быстрее других: изменение версии 9x, копирование версии 13x. Отлично @rgchris! - person giuliolunati; 05.04.2014
comment
@earl Думаю, я не обращал внимания. Как вы сказали, в обоих случаях достаточно просто удалить to string! и вернуть двоичный файл. И спасибо за ссылку :) - person rgchris; 05.04.2014

Вот версия, которая должна быть немного быстрее и, по крайней мере, использовать меньше памяти.

latin1-to-utf8: func [
    "Transcodes a Latin-1 encoded string to UTF-8"
    bin [binary!] "Bytes of Latin-1 data"
] [
    to binary! head collect/into [
        foreach b bin [
            keep to char! b
        ]
    ] make string! length? bin
]

Он использует преимущества символов Latin-1, имеющих те же числовые значения, что и соответствующие кодовые точки Unicode. Если вы хотите преобразовать другой набор символов, для которого это не так, вы можете выполнить расчет на b, чтобы переназначить символы.

Он использует меньше памяти и работает быстрее по ряду причин:

  • Обычно collect создает блок. Мы используем collect/into и передаем ему строку в качестве цели. Строки используют меньше памяти, чем блоки целых чисел или символов.
  • Мы предварительно распределяем строку по длине входных данных, что экономит на перераспределениях.
  • Мы позволяем собственному коду Rebol преобразовывать символы вместо того, чтобы выполнять собственные вычисления.
  • В цикле меньше кода, поэтому он должен работать быстрее.

Этот метод по-прежнему загружает файл в память целиком сразу и по-прежнему генерирует промежуточное значение для сохранения результатов, но, по крайней мере, промежуточное значение меньше. Возможно, это позволит вам обрабатывать большие файлы.

Если причина, по которой вам нужна UTF-8, заключается в том, что вам нужно обработать файл как строку в Rebol, просто пропустите to binary! и верните строку как есть. Или вы можете просто обработать двоичные исходные данные, просто преобразовать байты в двоичном формате, используя to char! для каждого из них по мере продвижения.

person BrianH    schedule 13.02.2014
comment
Очень аккуратно, +1! особ. для запоминания соответствия кодовых точек Latin1. Также хороший пример полезности (ограниченного) целого числа! значения как элементы двоичного файла!. - person earl; 14.02.2014
comment
Обнаружено, что добавление в 2 раза быстрее, чем сбор/в. Смотрите мой ответ. - person giuliolunati; 30.03.2014

На данный момент ничего не встроено, извините. Вот простая реализация преобразования Latin-1 в UTF-8, которую я написал и использовал с Rebol 3 некоторое время назад:

latin1-to-utf8: func [
    "Transcodes a Latin-1 encoded string to UTF-8"
    bin [binary!] "Bytes of Latin-1 data"
] [
    to-binary collect [foreach b bin [keep to-char b]]
] 

Примечание. Этот код оптимизирован для удобочитаемости и ни в коем случае для производительности. (С точки зрения производительности это совершенно глупо. Вас предупредили.)

Обновление: Включена оптимизация @BrianH "байтовые значения Latin-1 соответствуют кодовым точкам Unicode", которая сводит вышеописанное к однострочному (и в то же время немного менее глупому). Еще. более оптимизированную версию использования памяти см. в хорошем ответе @BrianH.

person earl    schedule 12.02.2014
comment
@giuliolunati Обратите внимание, что в двоичные данные — это небольшая оболочка над тем, что я считаю лучшим выражением: в двоичные данные! данные. Используя TO непосредственно для типа данных, вы понимаете, что можете писать такие вещи, как в (либо условие [строка!] [бинарные!]) данные. Почему некоторые люди считают, что восклицательный знак, который они все время используют для указания типов, слишком раздражает и нуждается в оболочке, ускользает от меня, но некоторые это делают. Но если вам когда-нибудь интересно, является ли что-то встроенным или нет, вы можете использовать исходный код, например. source to-binary и убедитесь, что он вызывает TO и не является нативным! или действием! - person HostileFork says dont trust SE; 12.02.2014

person    schedule
comment
В отличие от других предложенных вариантов, эта версия возвращает не binary!, а string! (так что технически она только декодирует латиницу 1 в Unicode в памяти, но не кодирует ее снова в UTF-8). Чтобы это исправить, вам нужно изменить последнюю строку на to binary! t. - person earl; 05.04.2014