Как файл может содержать нулевые байты?

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

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

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

26
задан RK. 5 January 2016 в 20:50
поделиться

3 ответа

Строки с нулевым символом в конце - это конструкция C, используемая для определения конца последовательности символов, предназначенной для использования в качестве строки. Функции манипуляции со строками, такие как strcmp, strcpy, strchr и другие, используют эту конструкцию для выполнения своих обязанностей.

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

Вот пример того, как это работает:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fp = fopen("out1","w");
    if (fp == NULL) {
        perror("fopen failed");
        exit(1);
    }

    int a1[] = { 0x12345678, 0x33220011, 0x0, 0x445566 };
    char a2[] =  { 0x22, 0x33, 0x0, 0x66 };
    char a3[] = "Hello\x0World";

    // this writes the whole array
    fwrite(a1, sizeof(a1[0]), 4, fp);
    // so does this
    fwrite(a2, sizeof(a2[0]), 4, fp);
    // this does not write the whole array -- only "Hello" is written
    fprintf(fp, "%s\n", a3);
    // but this does
    fwrite(a3, sizeof(a3[0]), 12, fp);
    fclose(fp);
    return 0;
}

Содержимое out1:

[dbush@db-centos tmp]$ xxd out1
0000000: 7856 3412 1100 2233 0000 0000 6655 4400  xV4..."3....fUD.
0000010: 2233 0066 4865 6c6c 6f0a 4865 6c6c 6f00  "3.fHello.Hello.
0000020: 576f 726c 6400                           World.

Для первого массива, потому что мы используем функцию fwrite и сообщаем ей запишите 4 элемента размером int, все значения в массиве появятся в файле. Из вывода видно, что все значения записаны, значения 32-разрядные, а каждое значение записано в порядке байтов с прямым порядком байтов. Мы также видим, что каждый второй и четвертый элементы массива содержат один нулевой байт, а третье значение 0 имеет 4 нулевых байта, и все они появляются в файле.

Мы также используем fwrite для второго массива, который содержит элементы типа char, и мы снова видим, что все элементы массива появляются в файле. В частности, третье значение в массиве - 0, которое состоит из одного нулевого байта, который также появляется в файле.

Третий массив сначала записывается с помощью функции fprintf с использованием спецификатора формата %s, который ожидает строку. Он записывает первые 5 байтов этого массива в файл перед тем, как встретить нулевой байт, после чего прекращает чтение массива. Затем он печатает символ новой строки (0x0a) в соответствии с форматом.

Третий массив снова записывается в файл, на этот раз с помощью fwrite. Строковая константа "Hello\x0World" содержит 12 байтов: 5 для «Hello», один для явного нулевого байта, 5 для «World» и один для нулевого байта, который неявно завершает строковую константу. Поскольку fwrite дан полный размер массива (12), он записывает все эти байты. Действительно, глядя на содержимое файла, мы видим каждый из этих байтов.

В качестве примечания, в каждом из вызовов fwrite я жестко закодировал размер массива для третьего параметра вместо того, чтобы использовать более динамическое выражение, такое как sizeof(a1)/sizeof(a1[0]), чтобы более точно определить, сколько байты пишутся в каждом случае.

44
ответ дан dbush 5 January 2016 в 20:50
поделиться

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

C-строки представляют собой последовательность байтов, оканчивающихся нулевым байтом, просто вопрос соглашения. Они слишком часто являются источником путаницы; просто последовательность, оканчивающаяся нулем, означает, что любой ненулевой байт, оканчивающийся нулем, является правильной строкой C! Даже тот, который содержит непечатаемый байт или контрольный символ. Будьте осторожны, потому что ваш пример не C! В C printf("dummy\000foo"); никогда не будет печатать foo, поскольку printf будет рассматривать строку C, начинающуюся с d и заканчивающуюся нулевым байтом в середине. Некоторые компиляторы жалуются на такой строковый литерал C.

Теперь нет прямой связи между строками C (которые обычно также содержат только печатные символы) и текстовым файлом. В то время как печать строки C в файл обычно заключается в сохранении только ее подпоследовательности ненулевых байтов.

8
ответ дан Jonathan Leffler 5 January 2016 в 20:50
поделиться

Рассмотрим обычные вызовы функций C для записи данных в файлы - write(2) :

ssize_t
write(int fildes, const void *buf, size_t nbyte);

… и fwrite(3) :

size_t
fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);

Ни одна из этих функций не принимает const char * NUL-оканчивающуюся строку. Скорее, они принимают массив байтов (a const void *) с явным размером. Эти функции обрабатывают байты NUL точно так же, как и любое другое значение байта.

0
ответ дан 200_success 5 January 2016 в 20:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: