【读写和修改U盘中的十六进制dat数据文件】


前言

大学毕业后来到公司的第一件工作就是通过U盘插入装有安卓系统的机器,通过U盘向机器设置设备码(下称SN)。一开始我的思路就是通过InStream先取到U盘里文件的数据,然后通过OutStream修改文件,然后把读出来的SN通过封装好的api接口发到机器上就完事。思路大体是对的,但是中间过程折磨了我这个新手好几天,接下来我将对其中的每一个环节进行分析。

一、十六进制dat文件解析

如果大家也有做到关于读取字节文件dat的,一定一定一定要问你的负责人要到关于dat文件的格式说明!!!不然会超级麻烦。话不多说,先展示一下我的dat文件的格式:
在这里插入图片描述
上述图片就是我用到的sn.dat文件。这里就不提我的摸索痛苦经历了,直接上结论。
首先最左边一列(“空格”前那一串数字)并不是我们会读取到的数据,我的理解是一种序号并且在你读写文件的时候并不会影响到,相当于是隐形的,简单来说就是可以不用管,这东西给你是方便看的。当然空格也是,实际上并没有空格,都是用来方便看的。然后最右边呢一串像乱码一样的东西也不用管,我的负责人跟我说是类似注解说明一样的东西,所以这个dat文件实际对我们有用的部分就是中间这一大块数据。
我这个dat文件是十六进制的形式,我们把每一行分为4组,每一组8个数。拿一行的第一组为例:803E0000 我先说结论,每两个数表示一个十六进制数,每4个十六进制数也就是一组代表一个数,我这里的数据采用的是小端结构,所以读出来就是"0x00、0x00、0x3E、0x80",换成十进制就是16000。解释一下,一个int型数据有4个字节,4byte就是32位,一个十六进制的数据(0x3E 这样)是8位,所以4个十六进制数就代表了一个int型的数。然后关于什么是小端结构,小端结构说白了就是低位写在前面,我们平时写的数据就是相反的大端结构,是高位写在前面。在硬件底层一般数据传输用的都是小端结构,通讯协议用的是大端结构。具体的详解可以百度大端、小端模式。

二、十六进制dat文件读操作

在读dat文件之前,我先说明一下我的文件格式,前4个字节存的是读到第几个SN号,9-12个字节存放的是SN号总数。然后从第六行开始,每8个字节为一组,前4个字节是标志位,0代表SN没用过,1代表已使用过;后4个字节才表示SN号。

1.读取手机中的dat文件数据

这里与一般读写文件的区别在于我们是需要读写字节文件,代码如下。

  public static void changeDat(File file) {
        FileInputStream inStream = null;
        FileOutputStream outStream = null;
        SNFileStream snFileStream = new SNFileStream();
        int dataPos = 0;//SN码到的使用位置
        int dataCount = 0;//SN码总数
        byte[] bytes = new byte[220 * 1024];//获取sn.dat内容
        int SNFlag = 0;//标志位
        int SNValue = 0;//设备号
        String StrSNValue = "";
        File newFile = new File("/mnt/shared/Pictures/sn2.dat");


        try {
            inStream = new FileInputStream(file);
            outStream = new FileOutputStream(newFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //getChannel()该方法的返回类型为FileChannel,它返回与此流连接的FileChannel。
        ReadableByteChannel inCh = inStream.getChannel();
        {
            //分配直接字符缓冲区
            ByteBuffer buffer = ByteBuffer.allocateDirect(16);
            //ByteOrder.LITTLE_ENDIAN表示小端模式
            buffer.order(ByteOrder.LITTLE_ENDIAN);
            int num = 0;
            while (true) {
                try {
                    if (inCh.read(buffer) == -1) break;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                buffer.flip();
                bytes[num] = buffer.get();
                num++;
                buffer.compact();
            }
            //关闭FileInPutStream
            try {
                inStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

2.修改dat文件

把dat文件里的内容全部读出来存入byte数组后,拿出需要的数据并将需要修改的内容在byte数组中改,然后重新存入一个dat文件替代原来的文件,代码如下(接上)。

		//获取索引 4个byte代表一个32位的十六进制数
        byte[] mid = new byte[4];   //取32位十六进制数
        for (int i = 0; i < 4; i++) {
            mid[i] = bytes[i];
        }
        dataPos = snFileStream.byte2int(mid);
        Log.d("dataPos", String.valueOf(dataPos));

        //更新dataPos
        int updateDataPos = dataPos + 2;
        mid = snFileStream.int2byte(updateDataPos);
        for (int i = 0; i < 4; i++) {
            bytes[i] = mid[i];
        }

        //获取总数
        for (int i = 0; i < 4; i++) {
            mid[i] = bytes[i + 8];
        }
        dataCount = snFileStream.byte2int(mid);
        Log.d("dataCount", String.valueOf(dataCount));
        //获取SN码
        int SNValuePos = 4 + 8 * (dataPos - 1) + 80;
        for (int i = 0; i < 4; i++) {
            mid[i] = bytes[i + SNValuePos];
        }

        for (int i = 3; i >= 0; i++) {
            StrSNValue = StrSNValue.concat(DataTypeConvert.oneByte2TwoHexString(mid[i]));
        }
        Log.d("=-=-=-=-=-=-=-", StrSNValue);

        SNValue = snFileStream.byte2int(mid);
        Log.d("SNValue", String.valueOf(SNValue));
        //更新SN的标志位
        bytes[SNValuePos - 1] = 0x01;

        //把更新后的所有数据写入新dat文件中
        try {
            int length = bytes.length;
            Log.d("snLength", String.valueOf(length));
            outStream.write(bytes, 0, length);
            outStream.flush();
            outStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d("sn2.dat_Length", String.valueOf(newFile.length()));
        Log.d("sn1.dat_Length", String.valueOf(file.length()));

        //更新文件
        boolean successA = file.delete();
        if (successA) {
            Log.d("删除结果", "成功!!!");
        } else {
            Log.d("删除结果", "失败!!!");
        }
        File newFileName = new File("/mnt/shared/Pictures/sn.dat");
        boolean successB = newFile.renameTo(newFileName);
        if (successB)
            Log.d("重命名结果", "Success!!!");
        else
            Log.d("重命名结果", "Fail!!!");
    }

这段代码里有用到两个转换函数,是用来把byte和int互相转换,两个函数如下。

    public static byte[] int2byte(int intValue) {
        byte[] b = new byte[4];
        for (int i = 0; i < 4; i++) {
            b[i] = (byte) (intValue >> 8 * i & 0xFF);//低位在前
        }
        return b;
    }

    public static int byte2int(byte[] b) {
        int intValue = 0;
        byte[] tempArray = new byte[4];
        for (int i = 0; i < b.length; i++) {
            tempArray[i] = b[i];
        }
        for (int i = 3; i >= 0; i--) {
            intValue |= (tempArray[i] & 0xFF) << (8 * i);
        }
        return intValue;
    }

以上就是在手机本地读取和存储dat文件的方法,接下来介绍一下U盘的读取。

3.获取U盘权限

首先在读取U盘文件之前,需要申请U盘权限(前面也别忘记写上权限,一并写这里了)

	<!--表示支持usb设备-->
    <uses-feature android:name="android.hardware.usb.host" android:required="true"  />
    <uses-permission android:name="android.hardware.usb.host" android:required="false" />
    
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

在声明权限之后还需要动态获取U盘(USB设备的)的权限,然后用BroadcastReceiver根据接收到的不同广播去分别处理对应的事件,具体代码如下。

    //注册广播,监听otg插拔事件。
    public void registerReceiver() {
        IntentFilter usbDeviceStateFilter = new IntentFilter();
        //对应的USB设备插入的广播
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        //对应的USB设备拔出的广播
        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        registerReceiver(mUsbReceiver, usbDeviceStateFilter);
        //注册监听自定义广播
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(mUsbReceiver, filter);
    }

    //根据action,去分别处理对应的事件。
    BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            switch (action) {
                case ACTION_USB_PERMISSION://接收到自定义广播
                    synchronized (this) {
                        UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { //允许权限申请
                            if (usbDevice != null) {
                                //读取所有设备
                                read(getUsbMass(usbDevice));
                            }
                        } else {
                            ToastUtil.showLongToast(SystemInfoActivity.this, "用户未授权,读取失败");
                        }
                    }
                    break;
                case UsbManager.ACTION_USB_DEVICE_ATTACHED://接收到存储设备插入广播
                    UsbDevice device_add = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (device_add != null) {
                        ToastUtil.showLongToast(SystemInfoActivity.this, "接收到存储设备插入广播");
                        listDevice();
                    }
                    break;
                case UsbManager.ACTION_USB_DEVICE_DETACHED://接收到存储设备拔出广播
                    UsbDevice device_remove = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (device_remove != null) {
                        ToastUtil.showLongToast(SystemInfoActivity.this, "接收到存储设备拔出广播");
                        usbFile = null;
                    }
                    break;
            }
        }
    };

其中对应的事件为read()和listDevice()方法,代码如下。

    //读取USB设备的根目录
    public void read(UsbMassStorageDevice massDevice) {
        try {
            massDevice.init();//初始化
            Partition partition = massDevice.getPartitions().get(0);
            FileSystem currentFs = partition.getFileSystem();
            UsbFile root = currentFs.getRootDirectory();//获取根目录
            usbFile = root;
        } catch (Exception e) {
            e.printStackTrace();
            ToastUtil.showLongToast(SystemInfoActivity.this, "读取失败");
        }
    }

    public void listDevice() {
        //获取管理者
        UsbManager usbManager = (UsbManager) getContext().getSystemService(Context.USB_SERVICE);
        //枚举设备
        storageDevices = UsbMassStorageDevice.getMassStorageDevices(getContext());//获取存储设备
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        for (UsbMassStorageDevice device : storageDevices) {//可能有几个 一般只有一个 因为大部分手机只有1个otg插口
            if (usbManager.hasPermission(device.getUsbDevice())) {//有就直接读取设备是否有权限
                read(device);

            } else {//没有就去发起意图申请
                usbManager.requestPermission(device.getUsbDevice(), pendingIntent); //该代码执行后,系统弹出一个对话框
            }
        }
    }

    //获取USB设备
    private UsbMassStorageDevice getUsbMass(UsbDevice usbDevice) {
        for (UsbMassStorageDevice device : storageDevices) {
            if (usbDevice.equals(device.getUsbDevice())) {
                return device;
            }
        }
        return null;
    }

4.读写U盘dat文件

接下来就相对简单了,直接看代码。

    //把本地文件写入到U盘中
    public static void saveSDFile2OTG(File file, UsbFile usbFile) {
        UsbFile usbSNFile = null;
        //删除旧文件
        UsbFile[] usbFiles;
        try {
            usbFiles = usbFile.listFiles();
            if (usbFiles != null && usbFiles.length > 0) {
                for (UsbFile file1 : usbFiles) {
                    if (file1.getName().equals("sn.dat")) {
                        file1.delete();  //删除U盘旧的sn.dat
                        break;
                    }
                }
                usbSNFile = usbFile.createFile(file.getName());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            OutputStream os = new UsbFileOutputStream(usbSNFile);
            FileInputStream is = new FileInputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[1024 * 8];
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer);
            }
            os.flush();
            os.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        file.delete();//删除本机sn文件
    }


    //把U盘文件写入到本地文件中
    public static void saveOTGFile2SD(File f, UsbFile usbFile) {
        try {
            FileOutputStream os = new FileOutputStream(f);
            InputStream is = new UsbFileInputStream(usbFile);
            int bytesRead = 0;
            byte[] buffer = new byte[1024 * 8];
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
            os.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

然后看一下Main函数,我稍微简化了一下。

    //本地存放路径
    private static final String SDFILEPATH = "/storage/emulated/0/sn.dat";
    

        //动态获取U盘权限
        registerReceiver();
		//这里必须先要list一下,为了弹出对话框手动允许获得U盘读写权限。
        listDevice();
  		//U盘设置
        mBtnUdiskSetNum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (usbFile != null) {
                    try {
                        int isHasSNfile = 0;//判断是否含有SN文件
                        for (UsbFile file : usbFile.listFiles()) {
                            if ("sn.dat".equals(file.getName())) {
                                File sdfile = new File(SDFILEPATH);//在SD卡根目录创建一个临时sn.dat文件
                                isHasSNfile = 1;
                                new Thread(new Runnable() {
                                    @Override
                                    public void run() {
                                        saveOTGFile2SD(sdfile, file);//把U盘文件存入SD卡
                                        try {
                                        	//不停一下会报错,下同
                                            TimeUnit.SECONDS.sleep(2);
                                        } catch (InterruptedException e) {
                                            e.printStackTrace();
                                        }
                                        udiskReadSn.udiskInsertSN(SDFILEPATH, isSuccess, SNValue);//注入SN并修改sn.dat文件
                                        try {
                                            TimeUnit.SECONDS.sleep(2);
                                        } catch (InterruptedException e) {
                                            e.printStackTrace();
                                        }
                                        saveSDFile2OTG(sdfile, usbFile);//把修改好的sn.dat存回U盘
                                    }
                                }).start();
                            }
                            Log.i(TAG, file.getName());
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

总结

以上就是全部关于U盘读取和写入dat数据文件的全部,新人创作,觉得不戳点个赞呗~

参考文章

Java实现.dat文件转txt可读文件
Android OTG 读写U盘文件
android OTG (USB读写,U盘读写)最全使用相关总结

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值