Открыть/отредактировать заголовок utf8 в python (pyfits)

Мне приходится иметь дело с некоторыми файлами, которые содержат текст utf8 в заголовке. Это означает, что практически все функции пакета pyfits не работают. Также .decode не работает, поскольку заголовок подходит для класса, а не для списка. Кто-нибудь знает, как декодировать заголовок, чтобы я мог обрабатывать данные? Фактическое содержание не так важно, поэтому что-то вроде игнорирования писем вполне допустимо. Мой текущий код выглядит так:

hdulist = fits.open('Jupiter.FIT')
hdu = hdulist[0].header
hdu.decode('ascii', errors='ignore')

И я получаю: AttributeError: объект «Заголовок» не имеет атрибута «декодировать»

Такие функции, как:

print (hdu)

возвращаться:

ValueError: FITS header values must contain standard printable ASCII characters; "'Uni G\xf6ttingen, Institut f\xfcr Astrophysik'" contains characters/bytes that do not represent printable characters in ASCII.

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


person brium-brium    schedule 21.09.2016    source источник
comment
'Uni G\xf6ttingen, Institut f\xfcr Astrophysik' это не UTF-8, это (вероятно) кодировка Latin1 для 'Uni Göttingen, Institut für Astrophysik' ; эквивалент UTF-8 - 'Uni G\xc3\xb6ttingen, Institut f\xc3\xbcr Astrophysik'   -  person PM 2Ring    schedule 21.09.2016


Ответы (2)


Как указал анатолий техтоник, не-ASCII-символы в заголовках FITS совершенно недействительны и делают недействительными файлы FITS. Тем не менее, было бы неплохо, если бы astropy.io.fits мог хотя бы прочитать недопустимые записи. Поддержка этого в настоящее время нарушена и нуждается в чемпионе, чтобы исправить это, но никто этого не делает, потому что это достаточно нечастая проблема, и большинство людей сталкиваются с ней в одном или двух файлах, исправляют эти файлы и идут дальше. Хотя хотелось бы, чтобы кто-нибудь решил эту проблему.

Тем временем, поскольку вы точно знаете, на какой строке этот файл икает, я бы просто открыл файл в необработанном двоичном режиме и заменил строку. Если файл FITS очень большой, вы можете читать его по блокам и выполнять замену в этих блоках. Файлы FITS (особенно заголовки) записываются блоками по 2880 байт, поэтому вы знаете, что везде, где появляется эта строка, она будет выровнена по такому блоку, и вам не нужно выполнять какой-либо дополнительный анализ формата заголовка. Просто убедитесь, что строка, которой вы ее заменяете, не длиннее исходной строки, и что, если она короче, она дополняется пробелами справа, потому что заголовки FITS имеют формат фиксированной ширины, и все, что изменяет длину заголовка, будет испортить весь файл. Тогда для этого конкретного случая я бы попробовал что-то вроде этого:

bad_str = 'Uni Göttingen, Institut für Astrophysik'.encode('latin1')
good_str = 'Uni Gottingen, Institut fur Astrophysik'.encode('ascii')
# In this case I already know the replacement is the same length so I'm no worried about it
# A more general solution would require fixing the header parser to deal with non-ASCII bytes
# in some consistent manner; I'm also looking for the full string instead of the individual
# characters so that I don't corrupt binary data in the non-header blocks
in_filename = 'Jupiter.FIT'
out_filename = 'Jupiter-fixed.fits'

with open(in_filename, 'rb') as inf, open(out_filename, 'wb') as outf:
    while True:
        block = inf.read(2880)
        if not block:
            break
        block = block.replace(bad_str, good_str)
        outf.write(block)

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

После того, как это будет сделано, строго отговорите создателя файла — он не должен публиковать поврежденные файлы FITS.

person Iguananaut    schedule 22.09.2016
comment
Используя этот код, я получаю: SyntaxError: не-ASCII-символ '\xc3' в файле ....py в строке 103, но кодировка не объявлена; подробности см. на python.org/dev/peps/pep-0263. По сути, я не могу определить плохую строку в моем файле Python. - person brium-brium; 23.09.2016
comment
Я имею в виду, что я бы просто сделал это в интерпретаторе IPython или что-то в этом роде, но если вы хотите поместить это в файл, вы можете сделать, как сказано в PEP-263, и добавить # coding=utf-8 в начало файла. Я думаю, что в Python 3 это значение по умолчанию. - person Iguananaut; 24.09.2016
comment
Теперь я получаю: bad_str = 'Геттингенский университет, Институт астрофизики'.encode('latin1') UnicodeDecodeError: кодек 'ascii' не может декодировать байт 0xc3 в позиции 5: порядковый номер не в диапазоне (128) Извините, если я кажусь глупым. я совершенно новичок в python. - person brium-brium; 26.09.2016

Похоже, PyFITS просто не поддерживает его (пока?)

Из https://github.com/astropy/astropy/issues/3497:

FITS предшествует Unicode и никогда не обновлялся для поддержки чего-либо, кроме печатных символов ASCII для данных. Невозможно кодировать символы, отличные от ASCII, в заголовках FITS.

person anatoly techtonik    schedule 21.09.2016
comment
есть ли способ узнать, какие записи повреждены, и просто перезаписать их? - person brium-brium; 21.09.2016
comment
@ user6858243 можно фильтровать содержимое файла с помощью pymotw.com/2/codecs/#error- обработки, но я предлагаю вам спросить разработчиков, как правильно с этим справиться - github.com /astropy/astropy/issues/3497 - person anatoly techtonik; 22.09.2016