Все задачи

Имя, размер, содержание. Часть 2.

20 Aug 2013

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

 1 # Получает имя файла, 
 2 # читает файл и его метаданные,
 3 # возвращает ссылку на хеш { filename => '...', content => '...', size => NNN }.
 4 # Если файл не существует, content и size равны undef
 5 sub read_file_with_metadata 
 6 {
 7     my ($filename) = @_;
 8 
 9     return {
10         filename => $filename,
11         size => (stat($filename))[7],
12         content => scalar eval {scalar read_file($filename)},
13     };
14 }

И снова не все ладно: на этот раз неправильные значения иногда оказываются в поле size.

Пример кода можно скачать и поотлаживаться локально.

Подсказка

Показать

Снова проблемы с несуществующими файлами.

Подсказка-2

Показать

Пример неправильного результата:

{
    '' => undef,
    'filename' => '<nonexistent file>',
    'size' => 'content'
};

Подсказка-3

Показать

В случае неуспеха (например, файл не найден), stat возвращает пустой список.

Подсказка-4

Показать

Что будет, если взять 7-й элемент пустого списка? А 7-й элемент пустого массива?

Разоблачение

Показать

Во-первых, если бы программист использовал прагму warnings, он по крайней мере заметил бы предупреждение Odd number of elements in anonymous hash.

Во-вторых, в строчке 11 выбирается 7-й элемент из списочного литерала, не из переменной-массива. Хотя извлекается всего один элемент, на самом деле это срез (slice), а срезы списковых литералов ведут себя следующим образом: если все запрашиваемые индексы выходят за пределы списка, срез возвращает пустой список. Иначе (если хотя бы один индекс попал в размер списка) возвращаются элементы списка, дополненные undef-ами для слишком больших индексов.

Примеры со списковыми литералами:

()[0]      # ()
()[1,2]    # ()
("a")[0]   # ("a")
("a")[0,1] # ("a", undef)
("a")[1,2] # ()

Для сравнения – срезы массива:

@a = ();    @a[0];      # (undef)
@a = ();    @a[1,2];    # (undef, undef)
@a = ("a"); @a[0];      # ("a")
@a = ("a"); @a[0,1]     # ("a", undef)
@a = ("a"); @a[1,2]     # (undef, undef)

Подробнее про поведение срезов массивов и списковых литералов см. http://perldoc.perl.org/perldata.html#Slices.