문제

여러 다른 운영 체제 및 컴퓨터에서 작성하고 읽어야하는 파일 형식으로 작업하고 있습니다. 이러한 컴퓨터 중 일부는 X86 기계, 다른 컴퓨터는 X86-64이어야합니다. 다른 프로세서가 존재할 수도 있지만 걱정하지 않습니다. 아직.

이 파일 형식에는 다음과 같이 읽을 수있는 몇 가지 숫자가 포함되어야합니다.

struct LongAsChars{
    char c1, c2, c3, c4;
};

long readLong(FILE* file){
    int b1 = fgetc(file);
    int b2 = fgetc(file);
    int b3 = fgetc(file);
    int b4 = fgetc(file);
    if(b1<0||b2<0||b3<0||b4<0){
        //throwError
    }

    LongAsChars lng;
    lng.c1 = (char) b1;
    lng.c2 = (char) b2;
    lng.c3 = (char) b3;
    lng.c4 = (char) b4;

    long* value = (long*) &lng;

    return *value;
}

그리고 다음과 같이 작성했습니다.

void writeLong(long x, FILE* f){
    long* xptr = &x;
    LongAsChars* lng = (LongAsChars*) xptr;
    fputc(lng->c1, f);
    fputc(lng->c2, f);
    fputc(lng->c3, f);
    fputc(lng->c4, f);
}

이것은 내 컴퓨터에서 작동하는 것처럼 보이지만, 다른 사람들에게는 그렇지 않을 수도 있고 파일 형식이 컴퓨터마다 다를 수 있다고 우려합니다 (예 : 32 비트 vs 64 비트 컴퓨터). 내가 뭔가 잘못하고 있습니까? 숫자 당 상수 수의 바이트를 사용하려면 코드를 구현해야합니까?

대신 Fread (내 코드도 더 빨리 만들 수 있음)를 사용해야합니까?

도움이 되었습니까?

해결책

유형을 사용하십시오 stdint.h 동일한 수의 바이트를 얻거나 아웃 할 수 있도록합니다.

그런 다음 코드가 실제로 처리하지 않는 엔디 니스 문제를 다루는 데 방금 남았습니다.

aliased char*로 Long을 직렬화하면 Endianess가 다른 플랫폼에 대해 서면 파일의 다른 바이트 주문이 남습니다.

바이트를 그렇게 분해해야합니다.

char c1 = (val >>  0) & 0xff;
char c2 = (val >>  8) & 0xff;
char c3 = (val >> 16) & 0xff;
char c4 = (val >> 24) & 0xff;

그런 다음 다음과 같은 것을 사용하여 권장하십시오.

val = (c4 << 24) |
      (c3 << 16) |
      (c2 <<  8) |
      (c1 <<  0);

다른 팁

문제가 발생할 수도 있습니다 엔지니어. 왜 그런 것을 사용하지 않습니까? netCDF 또는 HDF, 어떤 휴대 성 문제가 발생할 수 있습니까?

문자가있는 구조를 사용하는 대신보다 수학적 접근 방식을 고려하십시오.

long l  = fgetc() << 24;
     l |= fgetc() << 16;
     l |= fgetc() <<  8;
     l |= fgetc() <<  0;

이것은 당신이 성취하려는 것에 대해 조금 더 직접적이고 명확합니다. 또한 더 많은 숫자를 처리하기 위해 루프로 구현할 수도 있습니다.

당신은 긴 int를 사용하고 싶지 않습니다. 이는 플랫폼마다 크기가 다를 수 있으므로 플랫폼 독립 형식의 스타터가 아닙니다. 파일에 저장해야 할 값 범위를 결정해야합니다. 32 비트가 아마도 가장 쉽습니다.

당신은 다른 플랫폼에 대해 걱정하지 않는다고 말합니다 아직. 나는 당신이 그들을 지원할 가능성을 유지하고 싶다는 것을 의미합니다.이 경우 파일 형식의 바이트 주문을 정의해야합니다. X86은 작은 엔디언이므로 이것이 최고라고 생각할 수 있습니다. 그러나 Big-Endian은 네트워킹에 사용되기 때문에 "표준"교환 순서입니다.

Big-Endian ( "Network Byte Order")에 가면 :

// can't be bothered to support really crazy platforms: it is in
// any case difficult even to exchange files with 9-bit machines,
// so we'll cross that bridge if we come to it.
assert(CHAR_BIT == 8);
assert(sizeof(uint32_t) == 4);

{
    // write value
    uint32_t value = 23;
    const uint32_t networkOrderValue = htonl(value);
    fwrite(&networkOrderValue, sizeof(uint32_t), 1, file);
}

{
    // read value
    uint32_t networkOrderValue;
    fread(&networkOrderValue, sizeof(uint32_t), 1, file);
    uint32_t value = ntohl(networkOrderValue);
}

실제로 두 가지 변수를 선언 할 필요조차 없으며, "값"을 동일한 변수에서 네트워크 주문으로 대체하는 것은 약간 혼란 스럽습니다.

"네트워크 바이트 순서"가 메모리에서 상호 교환 가능 (빅 엔디 언) 순서로 발생하는 비트 배열로 정의되기 때문에 작동합니다. C에 저장된 물체는 숯의 시퀀스로 취급 될 수 있기 때문에 노조를 엉망으로 만들 필요가 없습니다. NTOHL/HTONL이 무엇을위한 것이기 때문에, 엔디 니스에 대한 특별한 신기가 필요하지 않습니다.

이것이 너무 느리면 SIMD 등으로 Fiendishly 최적화 플랫폼 특유의 바이트 스왑 핑에 대해 생각할 수 있습니다. 또는 Little-Endian을 사용하면 대부분의 플랫폼이 Little-Endian이므로 "평균적으로"더 빠릅니다. 이 경우 "Little-Endian의 호스트"및 "Little-Endian to Host"기능을 작성하거나 찾아야합니다. 물론 X86은 아무것도하지 않습니다.

나는 가장 큰 교차 아키텍처 접근법은 stdint.h에 정의 된 UINTXX_T 유형을 사용하는 것이라고 생각합니다. 여기에서 Man Page를 참조하십시오. 예를 들어 INT32_T는 x86 및 x86-64에서 32 비트 정수를 제공합니다. 나는 모든 코드에서 기본적으로 이것을 기본적으로 사용하며 모든 *닉스에서 상당히 표준이므로 문제가 없었습니다.

가정합니다 sizeof(uint32_t) == 4, 거기 있습니다 4!=24 Little-Endian과 Big-Endian이 가장 두드러진 사례 인 가능한 바이트 주문이지만 다른 것들도 사용되었습니다 (예 : PDP-Endian).

다음은 스트림에서 32 비트 서명되지 않은 정수를 읽고 쓰는 기능입니다. 대표가 바이트 시퀀스 인 정수에 의해 지정된 임의의 바이트 순서에주의를 기울입니다. 0,1,2,3: endian.h, endian.c

헤더는 이러한 프로토 타입을 정의합니다

_Bool read_uint32(uint32_t * value, FILE * file, uint32_t order);
_Bool write_uint32(uint32_t value, FILE * file, uint32_t order);

그리고이 상수

LITTLE_ENDIAN
BIG_ENDIAN
PDP_ENDIAN
HOST_ORDER
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top