2017-10-03 17 views
4

여기 내 첫 번째 질문은 C++에서 이진 파일을 읽는 것에 대해 이야기하고 싶습니다. ID3 태그 라이브러리를 코딩하고 있습니다. 그를 처리하는 코드의 조각은 여기바이너리 ifstream을 읽는 동안 C++, 이상한 동작이 발생했습니다.

ID3 = 3 bytes = constant identifier 
0xXXXX = 2 bytes = version (MSB: major version, LSB: minor. eg: 0x0301 = v3.1) 
0xXX = 1 byte = some flags 
4*0xXX = 4 bytes = size 

것 :

char   id[4]; 
uint16_t  version; 
uint8_t  flags; 
uint32_t  size; 
std::ifstream _stream; 

_stream = std::ifstream(_filename, std::fstream::binary); 

_stream.read(id, 3); 
id[3] = 0; 
// process id 
_stream.read((char *)&version, 2); 
// process version 
_stream.read((char *)&flags, 1); 
// process flags 
_stream.read((char*)&size, 4); 
// process flags 
_stream.close(); 

모든 것이 잘 작동

나는 처음 10 바이트는 다음과 같습니다 바이너리 파일이있는 헤더를 구문 분석하고 있습니다 버전을 제외하고. 그것은 v3.0 (0x0300), 라고 말할 수 있습니다. 버전에서 설정된 값은 0x03입니다. 문자열의 끝으로 0x00을 고려할 것이므로 텍스트 모드에서이 동작을 이해할 것입니다.하지만 여기서는 바이너리로 읽고 있습니다. 그리고 숫자 형식을 사용하십시오.

다른 이상한 것은, 내가 2 번에 처리하면 내가 예를 들어, 작동 할 수 있습니다 : 버전의 값이 0x0300이 경우에

uint16_t version = 0; 
char  buff; 

_stream.read(&buff, 1); 
version = (buff << 8); 
_stream.read(&buff, 1); 
version |= buff; 

합니다.

첫 번째 방법이 제대로 작동하지 않는 이유는 무엇입니까? 내가 잘못하고 있니?

어쨌든 도와 주셔서 감사합니다.

건배!

+7

를 사용하여 바이트를 교환 "리틀 엔디안"와 "큰 엔디안 ". –

+0

먼저 파일 형식 (EBNF 표기법)을 정확하게 정의해야합니다. –

+0

제쳐두고, 플랫폼 독립적 인 코드를 찾는다면 바이트가 8 비트라는 보장은 없습니다 case는 아마도 고정 너비 정수 타입도 지원하지 않을 것입니다. – AndyG

답변

4

버전 필드는 부호없는 short가 아닌 서명되지 않은 두 바이트 (주 버전, 부 버전)로 구성됩니다. endianess 문제에서 맹 글링되지 않도록 두 버전 번호를 따로 읽어야합니다.

엔디안은 플랫폼에 따라 다릅니다. 당신이 메이저 버전과 마이너 버전을 결합한 단 하나의 단락을 읽으 려한다면, 그 문제를 해결할 수 있습니다. 그러나 결국 자신이 만든 문제를 해결하기 위해 깨끗하고 이해하기 쉬운 코드를 작성하지 마십시오.

+0

@HWalters 주어진 endianess의 바이트 스트림에서 숫자를 로컬 플랫폼으로 변환하는 도구가 있습니다. 'ntohs()'와 비슷합니다. 따라서 플랫폼 독립적 인 방식으로 메이저 및 마이너 버전을 포함하는 짧은 정보를 얻을 수 있습니다. 이 예에서는 두 숫자를 모두 독립적으로 읽는 것과 비교할 때 가치가 없습니다. – ypnos

+0

@ypnos, 실제로 당신 말이 맞아요, 나는 바이트 단위로 읽는 것을 끝냈습니다. 그것은 더 쉽고 읽기 쉽습니다. 하지만 사양에 쓰여진 방식대로 두 바이트로 분리되어 있다는 것을 알지 못했습니다. –

1

이것은 엔디안 문제와 같습니다. 그래서 무엇입니까? Wikipedia에 따르면

엔디안은 컴퓨터 메모리 또는 보조 저장 메모리의 레이아웃

카메라 예컨대 저장 바이트보다 큰 수치로 배열되는 순서를 의미한다 :

big-little-endian

Image origin

값을 일회성으로 읽으면 바이트가 다시 정렬됩니다. 아마도 작성된 방식과 읽는 방식이 일치하지 않기 때문일 수 있습니다.

당신은 그들이 메모리에 배치되는 순서를 알고 있기 때문에, 당신은 다음 중 하나를 수행해야합니다 바이트로

  1. 읽기 바이트.
  2. 는 값을 읽고 값을 읽어 GCC
  3. 에 대한
  4. 를 VC++에서 _byteswap_ushort 또는 __builtin_bswap16를 사용하여 바이트를 교환하고 여기에 대한 몇 가지 구글에서 음식의 custom implementation
+0

# 2하지 마십시오. 여기서 공급 업체별 확장을 사용할 필요가 없으며 코드를 이동성이 없도록 만들 수 있습니다. –

+1

@underscore_d, 사용자 정의 스왑 구현에 대한 참조를 추가했습니다. –

+0

@DanielTrugman 그것은 학교 수업에서 수업을 다시 상기시켜줍니다. 어떻게 그리워할까요 ... 나는 5 년 전에 그 실수를 저질렀습니다. 나는 고급 언어에 너무 많은 시간을 썼다고 생각한다. 어쨌든, 해답을 주셔서 감사합니다. –