2012-11-02 4 views
4

Java에서 이진 파일을 읽으려고합니다. 부호없는 8 비트 값, 부호없는 16 비트 값 및 부호없는 32 비트 값을 읽는 방법이 필요합니다. 이 작업을 수행하는 최선의 방법은 무엇입니까 (가장 빠르고 멋진 코드)? 나는 C++로 이런 짓을하고이 같은 짓습니다 :이 경우 예를 버퍼에 문제가 발생 4 바이트를 부호없는 32 비트 정수로 변환하고 long에 저장하십시오.

uint8_t *buffer; 
uint32_t value = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24; 

그러나 자바

를 [1]은이 비트 왼쪽의 결과로 설정 로그인 값을 포함 shift는 int (?)입니다. OR 대신 : 특정 장소에서 0xA5 만 보내면 OR : 0xFFFFA500 또는 그와 비슷한 것으로 두 개의 상위 바이트를 "손상"시킵니다.

나는 지금과 같이 보이는 코드를 가지고 :

public long getUInt32() throws EOFException, IOException { 
    byte[] bytes = getBytes(4); 
    long value = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); 
    return value & 0x00000000FFFFFFFFL; 
} 

나는 4 바이트 결과 0 × 50 0x67 0xA5 0x72을 변환 할 경우

대신 0x5072A567의 0xFFFFA567입니다.

편집 :

public long getUInt32() throws EOFException, IOException { 
    byte[] bytes = getBytes(4); 
    long value = bytes[0] & 0xFF; 
    value |= (bytes[1] << 8) & 0xFFFF; 
    value |= (bytes[2] << 16) & 0xFFFFFF; 
    value |= (bytes[3] << 24) & 0xFFFFFFFF; 
    return value; 
} 

을하지만이 할 수있는 더 좋은 방법이되지 않습니다 :이 위대한 작품? 10 비트 동작은 이와 같은 단순한 것보다 훨씬 "비트"가 많습니다. (내가 뭘했는지 보시라.) =)

+0

사용하는 변수가 긴 경우 ALU는 항상 64 비트의 연산을 수행합니다. 변수가 int 인 경우 ALU는 항상 32 비트에서 연산을 수행하고 (ALU 기능의 나머지 32 비트는 사용하지 않습니다). 바이트에 대한 연산은 ALU의 58 비트를 사용하지 않을 가능성이 높습니다. 이러한 작업은 항상 1 클럭 사이클에서 이루어 지므로 좋은 10 비트라는 "비트"가 너무 많지는 않습니다. –

+0

아니, 귀하의 작업 구현은 바로 올바른 접근 방식입니다. –

+1

위의 코드에서 마지막 비트 및 연산이 필요하지 않습니다. value | = (bytes [3] << 24) & 0xFFFFFFFF; –

답변

1

당신은 올바른 생각을 가지고 있습니다. 나는 명백한 개선이 있다고 생각하지 않습니다. java.io.DataInput.readInt spec을 보면, 그들은 똑같은 코드를 가지고 있습니다. 그들은 <<&의 순서를 바꿉니다. 당신이 방법이에 대한 잔인한 메모리 맵 영역을 사용하지 않는

byte 배열에서 한 가지의 int을 읽을 수있는 방법이 없습니다.

물론, 직접 대신 먼저 byte[]에 독서의 DataInputStream를 사용할 수 있습니다 반대 엔디 언에

DataInputStream d = new DataInputStream(new FileInputStream("myfile")); 
d.readInt(); 

DataInputStream 작품을 사용하는 것보다, 당신은 또한 일부 Integer.reverseBytes 전화를해야합니다 그래서. 더 빨라지는 않을 것이지만 더 깨끗합니다.

2

샘플 코드의 문제점은 암시 적으로 바이트에서로 변환 할 때 부호 확장을 사용하여 수행하므로 바이트의 첫 번째 비트가 1이면 0이 아닌 1로 채워집니다. 부호 확장을 방지하는 long 변환을 사용하면 코드가 완벽하게 작동 할 수 있습니다.

public static long byteAsULong(byte b) { 
    return ((long)b) & 0x00000000000000FFL; 
} 

public static long getUInt32(byte[] bytes) { 
    long value = byteAsULong(bytes[0]) | (byteAsULong(bytes[1]) << 8) | (byteAsULong(bytes[2]) << 16) | (byteAsULong(bytes[3]) << 24); 
    return value; 
} 

신중할 경우 서명 된 값을 사용하여 비트를 포함 할 수 있습니다. 피할 필요가있는 것은 산술 및 부호있는 비트 시프 팅과 같은 형식 또는 서명 된 연산입니다. 값을 숫자로 인쇄해야하는 경우, 모든 내장 된 Java 방법으로 큰 부호없는 숫자가 음수로 나타납니다.

모든 것을 알아야 할 가장 중요한 점은 비트 이동에 관한 것입니다. 오른쪽으로 이동하면 >> 연산자는 2의 칭찬으로 숫자의 부호를 유지합니다. 이것은 가장 왼쪽의 비트가 1 인 경우, 0이 아닌 1이 될 비트가 시프트됨을 의미합니다. 좋은 소식은 자바는 적어도 부호없는 비트 시프 팅 연산자를 가지고 있다는 것인데, 항상 0으로 시프트 될 연산자는 >>>입니다.예 :

int bits; 
bits >>> 4; 

비트 더미가 표현하는 데이터는 임의적임을 항상 기억하십시오. 자바의 내부 메쏘드는 모두 비트를 2의 칭찬으로 취급하지만, 그 중 아무 것도 사용하지 않으면 부호있는 바이트는 사용자가 넣은 것과 동일한 비트를 포함합니다.

1

보다 일반 버전의 첫 번째 정수로 자신의 부호없는 값으로 바이트를 변환 :

public long getUInt32() throws EOFException, IOException { 
    byte[] bytes = getBytes(4); 
    long value = 
     ((bytes[0] & 0xFF) << 0) | 
     ((bytes[1] & 0xFF) << 8) | 
     ((bytes[2] & 0xFF) << 16) | 
     ((bytes[3] & 0xFF) << 24); 
    return value; 
} 

는 비트 연산의 수에 집착하지 마십시오, 가장 가능성이 컴파일러는 바이트 운영에 사람들을 최적화합니다.

또한 부호를 피하기 위해 32 비트 값에 long을 사용하지 말아야하며 int을 사용하고 대부분의 시간에 서명되어 있다는 사실을 무시할 수 있습니다. this answer을 참조하십시오.

관련 문제