C&;中的fwrite();Java中的readInt()的结尾不同

C&;中的fwrite();Java中的readInt()的结尾不同,java,android,c,endianness,datainputstream,Java,Android,C,Endianness,Datainputstream,本机代码: 使用fwrite()写入数字27。 int main() { int a = 27; FILE *fp; fp = fopen("/data/tmp.log", "w"); if (!fp) return -errno; fwrite(&a, 4, 1, fp); fclose(); return 0; } 使用DataInputStream.readInt()读回数据(27): public int readIntDataInput

本机代码:

使用fwrite()写入数字27。

int main()
{
  int a = 27;
  FILE *fp;
  fp = fopen("/data/tmp.log", "w");
  if (!fp)
     return -errno;

  fwrite(&a, 4, 1, fp);
  fclose();
  return 0;
}
使用DataInputStream.readInt()读回数据(27):

public int readIntDataInputStream(void)
{
   String filePath = "/data/tmp.log";
   InputStream is = null;
   DataInputStream dis = null;
   int k;

   is = new FileInputStream(filePath);
   dis = new DataInputStream(is);
   k = dis.readInt();
   Log.i(TAG, "Size : " + k);
   return 0;
}
O/p

十六进制是
0x1b000000

0x1b
27
。但是readInt()将数据读取为big-endian,而我的原生编码将数据写入little-endian。因此,我得到的不是
0x0000001b
,而是
0x1000000


我的理解正确吗?以前有人遇到过这个问题吗?

来自Javadoc for
readInt()

此方法适用于读取接口
DataOutput

如果您想读取由C程序编写的内容,您必须使用
java.nio
中的工具自己进行字节交换。我从来没有这样做过,但我相信你会将数据读入一个
ByteBuffer
,将缓冲区的顺序设置为
ByteOrder.LITTLE_ENDIAN
,然后在
ByteBuffer
上创建一个
IntBuffer
视图,如果你有一个值数组,或者只使用
ByteBuffer\getInt()


除此之外,我同意@EJP的观点,即数据的外部格式应该是big-endian,以实现最大的兼容性。

您的代码中存在多个问题:

  • 假设
    int
    的大小为
    4
    ,这不一定是真的,因为您想要处理32位int,所以应该使用
    int32\u t
    uint32\u t

  • 为了可靠地写入二进制数据,必须更多地以二进制方式打开文件。上述代码将在Windows上失败,以获得不那么琐碎的输出。使用
    fopen(“/data/tmp.log”,“wb”)

  • 你必须处理endianness。您正在使用该文件在可能具有不同本机端号和/或端号特定API的不同平台之间交换数据。Java似乎使用big-endian,也称为网络字节顺序,因此您应该使用
    hton32()
    实用程序函数在C平台上转换值。这不太可能对PC端的性能产生重大影响,因为此函数通常是内联扩展的,可能是作为一条指令,而且大部分时间都将花费在等待I/O上

以下是代码的修改版本:

#include <endian.h>
#include <stdint.h>
#include <stdio.h>

int main(void) {
    uint32_t a = hton32(27);
    FILE *fp = fopen("/data/tmp.log", "wb");
    if (!fp) {
        return errno;
    }
    fwrite(&a, sizeof a, 1, fp);
    fclose();
    return 0;
}
#包括
#包括
#包括
内部主(空){
uint32_t a=hton32(27);
文件*fp=fopen(“/data/tmp.log”,“wb”);
如果(!fp){
返回errno;
}
fwrite(&a,大小为a,1,fp);
fclose();
返回0;
}

是的,你是对的。C将以CPU的尾数写,这对于x86处理器来说是小尾数。他总是读大端文。解决方案:决定你的文件应该有哪一个尾端,并确保两者都相应地起作用。更重要的是,决定文件应该是big-endian,这使得它可以移植并与Java兼容,并相应地调整C代码。在这个C代码中你需要的是
inta=htonl(27)谢谢@Andreas。我有大量的数据要写。如何在C中有效地处理这个问题?@EJP实际上我有大量的数据,这些数据是从本机代码写入文件并从Java应用程序读取的。。是否有推荐的方法?另请参见
ByteBuffer
has a以
int
的形式读取给定尾数下的4个字节
IntBuffer
视图仅在所有数据都是
int
时有用,例如,如果它是
int[]
。您好,谢谢您的回答。关于第1点和第2点,我知道这些事情。另外,disz只是一个测试代码。第1点->我把它当作4个字节,因为前面提到过,java的readInt()将读取4个字节。2->我为unix系统工作。在Unix系统中,fopen中的“b”没有任何意义。在手册页中,“这是为了与C89完全兼容,没有效果;在所有符合POSIX标准的系统(包括Linux)上,'b'被忽略。”但是,这些都是使程序看起来更简洁的优点。谢谢。@mk..:我知道发布的代码只是一个快速而肮脏的测试。我总是试图提供一个详细的答案,不仅是OP,而且其他读者看到所有潜在的问题
“wb”
在大多数Unix平台上严格等同于
“w”
,但使用
b
并没有什么坏处,更明显的是
“/data/tmp.log”
是一个二进制文件,其名称并不意味着它
int
在绝大多数Unix系统上是32位长的,但是
long
(java中为64位)的大小在不同的ABI上有所不同,即使在同一主机上(32位模式与64位模式)。优雅应该成为第二天性。
#include <endian.h>
#include <stdint.h>
#include <stdio.h>

int main(void) {
    uint32_t a = hton32(27);
    FILE *fp = fopen("/data/tmp.log", "wb");
    if (!fp) {
        return errno;
    }
    fwrite(&a, sizeof a, 1, fp);
    fclose();
    return 0;
}