2013-11-07 3 views
1

Контекст

У меня есть питон код, который я использую для чтения метаданных из видеофайла с помощью ffprobe. Я запускаю команду ffprobe в качестве подпроцесса, собирая stdout (который находится в формате JSON), а затем преобразовываю его в словарь python. Код выглядит примерно так:набор символов преобразования Python

query = FFprobeQuery() 
stdout, stderr = query.run(video) 

Это работает в ffprobe команду в качестве подпроцесса вызова. Команда ffprobe является:

ffprobe -print_format json -show_streams "video.mov" 

стандартный вывод является:

"streams": [ 
    { 
     "index": 0, 
     "codec_name": "h264", 
     "codec_long_name": "H.264/AVC/MPEG-4 AVC/MPEG-4 part 10", 
     "codec_type": "video", 
     "codec_time_base": "1/2000000", 
     "codec_tag_string": "avc1", 
     "codec_tag": "0x31637661", 
     "width": 960, 
     "height": 540, 
     "has_b_frames": 0, 
     "pix_fmt": "yuv420p", 
     "level": 31, 
     "is_avc": "1", 
     "nal_length_size": "4", 
     "r_frame_rate": "1000000/41667", 
     "avg_frame_rate": "55000000/2291667", 
     "time_base": "1/1000000", 
     "start_time": "0.000000", 
     "duration": "2.291667", 
     "bit_rate": "2090617", 
     "nb_frames": "55", 
     "tags": { 
      "creation_time": "2013-11-04 09:38:31", 
      "language": "eng", 
      "handler_name": "Apple ╠µ╔Ý╩²¥¦┤ª└Ý│╠ð‗" 
     } 
    } 
] 

Затем преобразовать вышеупомянутую строку (стандартный вывод) в словарь питона, используя json пакет:

video_data = json.loads(stdout) 

Ошибка

Процесс, описанный выше, обычно работает нормально, но в этом случае UnicodeDecodeError поднят по вызову json.loads(stdout). Я не включил всю трассировку стека, но ошибка выглядит примерно так.

UnicodeDecodeError: 'utf8' codec can't decode byte 0xcc in position 6: invalid continuation byte 

Важно отметить, что в данном конкретном случае, видеофайл был отправлен из Китая, поэтому я предполагаю, есть символы в строке стандартного вывода, которые вызывают ошибку, поднятую json.loads(stdout) вызова. Мое предположение - это значение имени обработчика Apple ╠µ╔Ý╩²¥¦┤ª└Ý│╠ð‗.

Решения

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

import json 
import chardet 

detection = chardet.detect(stdout) 
encoding = detection.get('encoding') 
decoded_stdout = stdout.decode(encoding) 

video_data = json.loads(decoded_stdout) 

Вопросы

Я ничего не знаю о наборах символов, так что я пытаюсь:

1. Определите, применил ли я правильный подход?

2. Поймите, почему код преобразования, который я написал, действительно работает?

3. Определить, может ли проблема быть решена с использованием стандартной библиотеки python?

Все комментарии высоко оценены.

+1

Сначала прочтите следующее: http://www.joelonsoftware.com/articles/Unicode.html –

+0

@ Peter DeGlopper. Очень информативно! Благодаря! Я всегда буду стремиться включать определение Content-Type или какой-либо другой тег, описывающий кодировку набора символов для текстовых данных. – Yani

ответ

1

Во-первых, разъяснение: ваш подход не конвертируется с GB2312 в ASCII - и вы также не хотите этого, поскольку ASCII не может представлять строку '╠µ╔Ý╩²¥¦┤ª└Ý│╠ð‗'. То, что возвращает decode, представляет собой последовательность абстрактных символов, которые не могут быть непосредственно представлены на диске - кодирование является правилом сериализации.Этот тип называется unicode в Python 2 и str в Python 3; тип stdoutstr будет в Python 2, и bytes в Python 3.

Передача необработанных байтов в json.loads попыток deserialise (декодирование) ввод в строку символов с помощью UTF-8. Это дает ошибку, которую вы видите, поскольку ваш ввод сериализуется с использованием другой, несовместимой, кодировки. Сначала декодирование - это правильный подход - и в более новых версиях Python json.loads требует, чтобы вы это делали (он строго хочет последовательность символов, а не последовательность байтов).

Существует одна оговорка: угадывание кодировки, как это делает chardet, является hard и потенциально подвержено ошибкам. Это работает в этом конкретном случае, но у вас нет гарантии, что он будет работать, если вам нужно сделать что-то подобное с другими файлами. Это may - лучший подход, доступный вам - обычно вы ожидаете увидеть кодировку, упомянутую в начале метаданных файла, но, похоже, это не так. Но вы всегда должны пытаться найти какую-то авторитетную информацию, прежде чем прибегать к догадкам.

+0

@ Ivc. Благодаря! Отлично! Я не уверен, что понимаю все то, что вы только что сказали, но я думаю, что я извлек некоторые полезные вещи. Кажется, что реальная проблема заключается в том, что исходные данные не содержат информацию кодирования, необходимую для правильного ее анализа. Следовательно, я вынужден угадать кодировку. Как бы то ни было, кажется, что мое решение - необходимое зло. – Yani

+0

Я немного не уверен в разъяснении части вашего ответа, но, возможно, я оставил некоторые важные сведения. Абстрактные символы, на которые вы ссылаетесь, выводятся командой ffprobe, но как только они были записаны как stdout, они выглядят следующим образом: 'Apple \ xcc \ xe6 \ xc9 \ xed \ xca \ xfd \ xbe \ xdd \ xb4 \ xa6 \ xc0 \ xed \ xb3 \ XCC \ xd0 \ xf2'. Другими словами, stdout имеет тип 'str'. Когда я использую chardet для обнаружения кодировки этой строки, он возвращает 'GB2312' и когда я использую' str.decode («GB2312») 'Я получаю кодировку' ascii'. Не могли бы вы снова объяснить первую часть своего ответа? – Yani

+0

@Yani check 'type (stdout)' и 'type (stdout.decode (" GB2312 "))' - они должны быть разными. Если вы прочитали статью, связанную с этим вопросом, тогда должно быть понятно, что Py2 'unicode'/Py3' str' является последовательностью кодов unencoded unicode, в то время как Py2 'str'/Py3' bytes' является последовательностью необработанные байты. 'decode' не * изменяет * кодировку (на ASCII), она * удаляет * it (давая последовательность кодовых точек, а не байтов). Эта ситуация немного ясна в Python 3, чем Python 2, из-за лучших имен типов и других улучшений. Рассмотрите возможность обновления, если сможете. – lvc

Смежные вопросы