2015-08-09 1 views
0

Я должен разработать подсистему для хранения определенных бизнес-данных в файле, и я бегу в проблему, но сначала несколько требований у меня есть:Сохранение обычного текста и информации байтов в том же файле - проблемы конверсии

  • Он должен быть 1 файл для всех данных.
  • Данные содержат как обычный текст, который должен быть читаемым человеком, так и байтовыми данными.
  • Данные байта могут быть огромными (и растут в будущем), поэтому я должен сделать его небольшим, если это возможно.

Я думал, что просто поместил все в строку, закодировал его с помощью UTF8 (формат, который скоро не исчезнет) и напишите его в файл. Проблема заключается в том, что UTF8 не позволяет использовать определенные байтовые комбинации и изменяет их, когда я позже снова прочитаю файл.

Вот пример кода, который показывает проблему:

// The charset we use to encode the strings/file 
    Charset charSet = StandardCharsets.UTF_8; 

    // The byte data we want to store (as ints here because in the app it is used as ints) 
    int idsToStore[] = new int[] {360, 361, 390, 391}; 

    // We transform our ints to bytes 
    byte[] bytesToStore = new byte[idsToStore.length * 4]; 
    for (int i = 0; i < idsToStore.length; i++) { 
     int id = idsToStore[i]; 
     bytesToStore[i * 4 + 0] = (byte) ((id >> 24) & 0xFF); 
     bytesToStore[i * 4 + 1] = (byte) ((id >> 16) & 0xFF); 
     bytesToStore[i * 4 + 2] = (byte) ((id >> 8) & 0xFF); 
     bytesToStore[i * 4 + 3] = (byte) (id & 0xFF); 
    } 
    // We transform our bytes to a String 
    String stringToStore = new String(bytesToStore, charSet); 

    System.out.println("idsToStore="+Arrays.toString(idsToStore)); 
    System.out.println("BytesToStore="+Arrays.toString(bytesToStore)); 
    System.out.println("StringToStore="+stringToStore); 
    System.out.println(); 

    // We load our bytes from the "file" (in this case a String, but its the same result) 
    byte[] bytesLoaded = stringToStore.getBytes(charSet); 
    // Just to check we see if the resulting String is identical 
    String stringLoaded = new String(bytesLoaded, charSet); 

    // We transform our bytes back to ints 
    int[] idsLoaded = new int[bytesLoaded.length/4]; 
    int readPos = 0; 
    for (int i = 0; i < idsLoaded.length; i++) { 
     byte b1 = bytesLoaded[readPos++]; 
     byte b2 = bytesLoaded[readPos++]; 
     byte b3 = bytesLoaded[readPos++]; 
     byte b4 = bytesLoaded[readPos++]; 
     idsLoaded[i] = (b4 & 0xFF) | (b3 & 0xFF) << 8 | (b2 & 0xFF) << 16 | (b1 & 0xFF) << 24; 
    } 

    System.out.println("BytesLoaded="+Arrays.toString(bytesLoaded)); 
    System.out.println("StringLoaded="+stringLoaded); 
    System.out.println("idsLoaded="+Arrays.toString(idsLoaded)); 
    System.out.println(); 

    // We check everything 
    System.out.println("Bytes equal: "+Arrays.equals(bytesToStore, bytesLoaded)); 
    System.out.println("Strings equal: "+stringToStore.equals(stringLoaded)); 
    System.out.println("IDs equal: "+Arrays.equals(idsToStore, idsLoaded)); 

Выход с UTF8 является:

idsToStore=[360, 361, 390, 391] 
    BytesToStore=[0, 0, 1, 104, 0, 0, 1, 105, 0, 0, 1, -122, 0, 0, 1, -121] 
    StringToStore=(can not be pasted into SO) 

    idsLoaded=[360, 361, 495, -1078132736, 32489405] 
    BytesLoaded=[0, 0, 1, 104, 0, 0, 1, 105, 0, 0, 1, -17, -65, -67, 0, 0, 1, -17, -65, -67] 
    StringLoaded=(can not be pasted into SO) 

    Bytes equal: false 
    Strings equal: true 
    IDs equal: false 

Если я изменю Charset к UTF16BE (< - BE является Big Endian) это тестовые работы! Проблема в том, что я не уверен, что UTF16BE просто работает для этого теста «случайно». Мне нужно знать, будет ли это работать всегда или нет. Или, может быть, есть лучший способ.

Я благодарен за любые рекомендации. Заранее спасибо.

+0

Не пытайтесь напрямую представлять байты в виде строк. Если вы хотите, чтобы файл был читаемым, вы должны использовать алгоритм кодирования текста, такой как base64, который займет больше места, но будет безопасно открываться в текстовых редакторах и передавать текстовые носители. Если вам нужны фактические двоичные данные в файле - это не будет читаемо для человека. – RealSkeptic

ответ

2

Единственный способ гарантировать, что ваша кодировка всегда будет работать, - это проверить ее со всей таблицей ASCII: записать массив байтов, содержащий все 256 возможных значений, и проверить, правильно ли он прочитан.

Но, возвращаясь к корню проблемы, я сомневаюсь, что кодирование всех данных в строку будет хорошо работать. String - это структура Unicode, ориентированная на содержащий читаемый текст (т. Е. Он может не содержать некоторых символов в 32-символьном коде ascii).

Вместо этого я бы подумал о структурированном файле BINARY: будучи двоичным, вы гарантируете, что он может содержать что-либо прозрачно. И, судя по всему, вы можете хранить на нем несколько видов данных. Например, было бы хорошо, если бы вы могли спроектировать структуру, состоящую из сегментов, и каждый сегмент имел заголовок для длины своих данных. Бинарные сегменты будут считываться через InputStream, а текстовые сегменты через Reader (с желаемой кодировкой).