Преобразование файла BinHex в обычный файл с использованием Java

У меня есть бинхекс файл. Этот файл должен быть преобразован в обычный читаемый файл с использованием кода Java. Я нашел аналогичный вопрос здесь,

Декодирование Binhex с использованием кода Java

Но ответ не работает.

Я попробовал Base64, файл конвертируется в какой-то другой формат, который не читается человеком.

Пожалуйста, помогите мне решить эту проблему.

Код, который я пробовал, приведен ниже.

File f = new File("Sample.pdf");
Base64 base64 = new Base64();
byte[] b = base64.decode(getBytesFromFile(f));          
FileOutputStream fos = new FileOutputStream("Dcode.pdf");           
fos.write(b);
fos.close();

public static byte[] getBytesFromFile(File file) throws IOException {
    InputStream is = new FileInputStream(file);
    long length = file.length();
    byte[] bytes = new byte[(int)length];
    int offset = 0;
    int numRead = 0;
    while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) 
    {
        offset += numRead;
    }
    if (offset < bytes.length) {
        throw new IOException("Could not completely read file "+file.getName());
    }
    is.close();
    return bytes;
}

Файл Sample.pdf имеет кодировку BinHex. Я хочу, чтобы файл был декодирован.


person user1332962    schedule 28.09.2012    source источник
comment
Какой у вас код? Что не работает в вашем коде? Stack Overflow может быть крутым и все такое, но у него еще нет хрустального шара.   -  person FThompson    schedule 28.09.2012
comment
Я попробовал декодирование base64, как показано ниже. Base64 base64 = новый Base64(); byte[] b = base64.decode(getBytesFromFile(f)); FileOutputStream fos = новый FileOutputStream(Dcode.pdf); фос.написать(б); fos.close();   -  person user1332962    schedule 28.09.2012
comment
Добавьте его к своему вопросу (а также к результату исключения/проблемы).   -  person FThompson    schedule 28.09.2012
comment
Поскольку формат BinHex используется на практике во многих местах, я ожидаю, что в Интернете появится ряд ресурсов с кодировкой BinHex. Поскольку использование официального программного обеспечения BinHex для декодирования таких файлов требует доступа к Mac, насколько я понимаю, это не всегда вариант, поэтому возможность декодировать эти вещи с помощью Java имеет смысл. Я немного удивлен, что этот вопрос был закрыт, и я проголосую за его повторное открытие.   -  person MvG    schedule 02.10.2012


Ответы (1)


Отличие от Base64

Из того, что я нашел в Интернете, существуют разные версии формата BinHex. Ни один из них не совпадает с Base64. Однако есть большое сходство. Взяв, к примеру, спецификации BinHex 4.0, мы видим, что основные двоичное содержимое кодируется с использованием схемы кодирования с основанием 64, таким образом кодируя 3 октета в 4 символа. Однако он использует другой алфавит:

BinHex: !"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr
Base64: ABCDEFGHIJKLMNOPQERTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

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

Помимо основной части двоичного содержимого, в формат включены некоторые дополнительные метаданные. Согласно спецификации, это включает в себя разделители между данными и содержимым ветки ресурсов, контрольные суммы и некоторые другие биты информации.

Реализация

Следующий код декодирует входной файл BinHex и записывает его ответвление данных в выходной файл. Достаточно легко настроить этот код под свои нужды.

import java.io.*;
import java.nio.charset.Charset;

public class BinHexDec {

    private Charset charset;

    private String filename;

    private int dbegin, dend, rbegin, rend;

    public BinHexDec(String... args) throws IOException {
        InputStream in = System.in;
        OutputStream out = System.out;
        charset = Charset.defaultCharset();
        if (args.length >= 1)
            in = new FileInputStream(args[0]);
        if (args.length >= 2)
            out = new FileOutputStream(args[1]);
        if (args.length >= 3)
            charset = Charset.forName(args[2]);
        Reader reader = new InputStreamReader(in, charset);
        byte[] data;
        data = unbase(reader);
        reader.close();
        if (data.length < 27)
            throw new IOException("Too little data");
        data = unrle(data);
        parse(data);
        out.write(data, dbegin, dend - dbegin);
    }

    private byte[] unbase(Reader in) throws IOException {
        String digits = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ" +
            "[`abcdefhijklmpqr";
        assert digits.length() == (1 << 6);
        int[] value = new int[128];
        for (int i = 0; i < value.length; ++i)
            value[i] = -1;
        for (int i = 0; i < digits.length(); ++i)
            value[digits.charAt(i)] = i;

        int state = 0;
        int accum = 0;
        int alen = 0;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while (true) {
            int chr = in.read();
            if (chr == -1)
                throw new EOFException();
            switch (state) {
            case 0:
                switch (chr) {
                case '\n':
                case '\r':
                case '\t':
                case ' ':
                    state = 1;
                    break;
                }
                break;
            case 1:
                switch (chr) {
                case '\n':
                case '\r':
                case '\t':
                case ' ':
                    break;
                case ':':
                    state = 2;
                    break;
                default:
                    state = 0;
                }
                break;
            case 2:
                int v = -1, c = chr & 0x7f;
                if (c == chr) {
                    v = value[c];
                    if (v != -1) {
                        accum = (accum << 6) | v;
                        alen += 6;
                        if (alen > 8) {
                            alen -= 8;
                            baos.write((byte)(accum >>> alen));
                        }
                        break;
                    }
                }
                if (chr == ':') {
                    return baos.toByteArray();
                }
                break;
            }
        }
    }

    private static final byte RLE_MARK = (byte)0x90;

    private byte[] unrle(byte[] in) throws IOException {
        int len = in.length;
        if (in[0] == 0x90)
            throw new IOException("Incomplete RLE at beginning");
        for (int i = 0; i < in.length; ++i) {
            if (in[i] == RLE_MARK) {
                ++i;
                if (i == in.length)
                    throw new IOException("Incomplete RLE at end");
                int cnt = in[i] & 0xff;
                if (cnt == 0)
                    len -= 1;
                else
                    len += cnt - 3;
            }
        }
        byte[] out = new byte[len];
        for (int i = 0, o = 0; i < in.length; ++i) {
            if (in[i] == RLE_MARK) {
                ++i;
                int cnt = in[i] & 0xff;
                if (cnt == 0)
                    out[o++] = RLE_MARK;
                else {
                    byte b = out[o - 1];
                    for (int c = 1; c < cnt; ++c)
                        out[o++] = b;
                }
            }
            else {
                out[o++] = in[i];
            }
        }
        return out;
    }

    private int get4(byte[] data, int offset) {
        return ((data[offset    ] & 0xff) << 24)
            |  ((data[offset + 1] & 0xff) << 16)
            |  ((data[offset + 2] & 0xff) <<  8)
            |  ((data[offset + 3] & 0xff)      );
    }

    private int get2(byte[] data, int offset) {
        return ((data[offset] & 0xff) << 8)
            |  ((data[offset + 1] & 0xff));
    }

    private void crcCheck(byte[] data, int begin, int end) throws IOException {
        int crc = 0;
        for (int i = begin; i < end; ++i) {
            crc = ((crc  << 4)&0xffff) ^ (crc >> 12)*0x1021;
            crc = ((crc  << 4)&0xffff) ^ (crc >> 12)*0x1021;
            crc = crc ^ (data[i] & 0xff);
        }
        crc = ((crc  << 4)&0xffff) ^ (crc >> 12)*0x1021;
        crc = ((crc  << 4)&0xffff) ^ (crc >> 12)*0x1021;
        crc = ((crc  << 4)&0xffff) ^ (crc >> 12)*0x1021;
        crc = ((crc  << 4)&0xffff) ^ (crc >> 12)*0x1021;
        int expected = get2(data, end);
        if (expected != crc)
            throw new IOException("CRC mismatch");
    }

    private void parse(byte[] in) throws IOException {
        int namelen = in[0] & 0xff;
        int headlen = namelen + 20;
        int dlen = get4(in, headlen - 8);
        int rlen = get4(in, headlen - 4);
        if (dlen < 0 || rlen < 0)
            throw new IOException("Unsigned data lenhgths not supported");
        dbegin = headlen + 2;
        dend = dbegin + dlen;
        rbegin = dend + 2;
        rend = rbegin + rlen;
        if (in.length != rend + 2 &&
            (in.length != rend + 3 || in[rend + 2] != 0))
            throw new IOException("Incorrect data size:" +
                                  " expected " + (rend + 2) +
                                  " but got " + in.length + " bytes");
        crcCheck(in, 0, headlen);
        crcCheck(in, dbegin, dend);
        crcCheck(in, rbegin, rend);
        filename = new String(in, 1, namelen, charset);
        System.err.println(filename);
    }

    public static void main(String[] args) throws IOException {
        new BinHexDec(args);
    }

}
person MvG    schedule 28.09.2012
comment
Большое спасибо. Этот код великолепен. Это отлично сработало для моего требования. - person user1332962; 11.10.2012