【问题标题】:Getting wrong bytes from Java compared to C#与 C# 相比,从 Java 获取错误字节
【发布时间】:2015-01-26 16:09:09
【问题描述】:

所以我有一些 FRX 二进制文件,我试图使用 Java 的二进制读取方法从中获取字符串标题。

我有能力这样做,并使用以下程序指定在 C# 中读取字节的区域:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

    public class GetFromFRX
    {
        public static void Main()
        {
            StringBuilder buffer = new StringBuilder();
            using (BinaryReader b = new BinaryReader(File.Open("frmResidency.frx", FileMode.Open)))
            {
                try
                {
                    b.BaseStream.Seek(641, SeekOrigin.Begin);
                    int length = b.ReadInt32();

                    for (int i = 0; i < length; i++)
                    {
                        buffer.Append(b.ReadChar());
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine( "Error obtaining resource\n" + e.Message);
                }

            }
            Console.WriteLine(buffer);
        }
    }

问题更新: 尝试在 Java 中做同样的事情,我构建了以下程序。现在我已经实现了 Guava 以使用 LittleEndian 等价物,但是现在我的长度打印为 24,因此我只得到输出文件中的前 24 个字节。 ReadInt 是否不适合这种情况,并且功能与ReadInt32 不同?

import java.io.*;
import com.google.common.io.*;

public class RealJavaByteReader {

    public static void main(String[] args) throws IOException {

        FileInputStream in = null;
        FileOutputStream out = null;

        try {
            in = new FileInputStream("frmResidency.frx");
            LittleEndianDataInputStream din = new LittleEndianDataInputStream(in);
            out = new FileOutputStream("output.txt");

            int length = din.readInt();
            System.out.println(length);
            int c;

            for (c = 0; c < length; c++) {
                // TODO: first read byte and check for EOF
                out.write(din.read());
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

【问题讨论】:

  • Seek 是干什么用的?
  • 尝试实例化一个缓冲区,如 byte[] buffer = new byte[4];然后使用 out.read(buffer);将字节读入缓冲区。然后,您可以使用 System.out.println(DatatypeConverter.printHexBinary(buffer)); 打印缓冲区的内容。检查缓冲区的内容以查看您是否前进到正确的位置。在十六进制编辑器中打开它也可能会有所帮助。听起来您正在读取的 int 的第一个字节的前导位已设置。如果您希望更大的数据适合带符号的 int,您可能需要读取 4 个字节并手动转换为 long...
  • 在 C# 中,Seek 正在设置我的索引的起始位置,这是对应于十六进制值的十进制值,表示我需要的字符串标题的开始字节。
  • DataInput.readInt 采用大端格式; BinaryReader.ReadInt32 采用 little-endian 格式...所以这是 start 的问题。在您至少获得合适的长度之前,我不会再进一步​​。
  • 另外,在 C# 中,您将字符值写入StringBuilder,而在 Java 中,您将字节值写入文件。您似乎也没有对dout 做任何事情。

标签: java c# binary byte fileinputstream


【解决方案1】:

伊利西翁,

这可能是因为您可能正在读取使用 little endian 存储的 int。因此,Java 使用 Big endian 和 .NET little endian。

在java中使用如下函数将little endian int转换为big endian int。

/**
   * Byte swap a single int value.
   * 
   * @param value  Value to byte swap.
   * @return       Byte swapped representation.
   */
  public static int swap (int value)
  {
    int b1 = (value >>  0) & 0xff;
    int b2 = (value >>  8) & 0xff;
    int b3 = (value >> 16) & 0xff;
    int b4 = (value >> 24) & 0xff;

    return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
  }

请尝试查看以下帖子。

Converting Little Endian to Big Endian

【讨论】:

  • 欢迎来到 SO :) 虽然这在技术上是一个有效的答案,但在某种程度上扩展它会很好。从您的链接中复制和粘贴相关部分将使这个答案质量更高。
  • 我选择使用 Guava 库来实现 LittleEndianDataInputStream,但我想这也可以!
【解决方案2】:

此时我意识到我的错误是什么。现在LittleEndianDataInputStream已经实现了,我可以正确使用SkipBytes来设置我的初始字节位置,并且会根据需要返回字符串标题。当然,我最初只会生成前 24 个字节,因为对于 FRX 文件中的某些给定属性,二进制文件的前 4 个字节中的任何内容都必须保持 24 的长度。我必须用skipBytes 设置偏移量才能产生任何有意义的东西,因为 FRX 文件中的属性长度以 4 个字节为一组存储,后面是包含该属性的那些字节。

例如,如果我设置din.skipBytes(308);,那么 FRX 文件中的第 308 到第 312 字节保存了我需要的 Caption 属性中字符串的字节长度(例如 140),由readInt 输出.因此接下来的 140 个字节将包含我需要的字符串,并且我的 for 循环将正确迭代。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    相关资源
    最近更新 更多