CF round#202 A (13.09.28)

本文探讨了电影院售票员如何在有限资源下,确保每位顾客都能顺利购买到电影票并获得正确的找零。通过分析不同面额的钞票组合,作者提出了一种简单而有效的解决方案,以确保交易过程的流畅性和效率。
A. Cinema Line
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

The new "Die Hard" movie has just been released! There are n people at the cinema box office standing in a huge line. Each of them has a single 100, 50 or 25 ruble bill. A "Die Hard" ticket costs 25 rubles. Can the booking clerk sell a ticket to each person and give the change if he initially has no money and sells the tickets strictly in the order people follow in the line?

Input

The first line contains integer n (1 ≤ n ≤ 105) — the number of people in the line. The next line contains n integers, each of them equals 25, 50 or 100 — the values of the bills the people have. The numbers are given in the order from the beginning of the line (at the box office) to the end of the line.

Output

Print "YES" (without the quotes) if the booking clerk can sell a ticket to each person and give the change. Otherwise print "NO".

Sample test(s)
Input
4
25 25 50 50
Output
YES
Input
2
25 100
Output
NO
Input
4
50 50 25 25
Output
NO


题意:

上映一部AV, 售价25日元;

钱币在这里按规矩只有三种面额: 25, 50 ,100

很多人排队购买, 初始状态下, 售片方没有钱找零

问, 按给出的队列, 能顺利得让每个人都买到片吗?


做法:

明显找钱的情况只有两种, 收到50的时候找25, 收到100的时候, 找25和50各一张, 即25 + 50 = 75;

并且, 没有情况是需要把100找出去的, 所以只要讨论25 和 50够不够用;

所以, 25 和 50面额的钱币, 收一张, 记一张, 并在找出去的时候记得减去一张;

没应付完一个顾客都要检查是否25 和 50面额的钱币不够用了~


AC代码:

#include<stdio.h>

int main() {
    int num25;
    int num50;
    int n;
    int mark;
    while(scanf("%d", &n) != EOF) {
        num25 = 0;
        num50 = 0;
        mark = 1;
        while(n--) {
            int num;
            scanf("%d", &num);
            if(num == 100) {
                num25--;
                num50--;
            }
            else if(num == 50) {
                num25--;
                num50++;
            }
            else if(num == 25)
                num25++;

            if(num25 < 0 || num50 < 0)
                mark = 0;
        }
        if(mark)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

2025-09-01 12:02:31.470 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:27.8 2025-09-01 12:02:31.470 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:27.8 2025-09-01 12:02:31.472 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.472 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.472 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.472 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.473 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.473 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.474 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.474 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:31.474 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解压 KVM_Navi_EU_148_1.zip: 1.7GB/3.0GB progress:51.4 2025-09-01 12:02:32.868 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:03:02.620 21339-21573 DecryptUtil com.kotei.overseas.navi I inputVerifyDataPath/data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_2.zip 2025-09-01 12:03:02.620 21339-21573 DecryptUtil com.kotei.overseas.navi I inputVerifyDataPath[/data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_2.zip] -> signPath[/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/] 2025-09-01 12:03:05.461 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:03:09.943 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:03:10.312 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:03:14.950 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:04:05.480 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:04:09.965 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:04:10.320 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:04:13.960 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:04:24.575 21339-21573 DecryptUtil com.kotei.overseas.navi I 原始 ZIP 文件已成功删除: /data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_2.zip 2025-09-01 12:04:24.575 21339-21573 DecryptUtil com.kotei.overseas.navi I aesDaraPath:/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/KVM_Navi_EU_148_2.zip.enc 2025-09-01 12:04:24.575 21339-21573 DecryptUtil com.kotei.overseas.navi I signDataPtah/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/KVM_Navi_EU_148_2.zip.enc.sig 2025-09-01 12:04:29.621 21339-21573 DecryptUtil com.kotei.overseas.navi I base64格式签名数据: g013oFHKCv0K+0I5nwbCToUOch9aIfSNLhgyDzJ30uM5gN8VlS/K3l5gAHtjMv/Lm0269M5vtAdsi/tJ3sPdFSkZrdyfxA2CFaxAZpOMuLe7AJEZfbvRX0+XY88lArYvivdfK/6gTGdGYiTSsFzvzxYvSd/yGmmJOZKP9zmAVySAhyTcAbqenxV4nIfefD9t3R/OSxlKqCJsKZiO800ffXO010s65U64XVqh0Fc+2I0PibzA2LyYJ2q+YObkABe9W2YDgXWq91bzPNtX+nSRV4T6773Mf+i4j2MedPIb7LwFwx5HD3eFUpmp+KY5jYFUO4anW5UemQDwJne/Xurstw== 2025-09-01 12:04:29.625 21339-21573 DecryptUtil com.kotei.overseas.navi I PUBLIC_KEY: OpenSSLRSAPublicKey{modulus=cb4a3563eed96eff8d354083c7e2cc066aacc8f1aca2343a3ed5f9d271c41c9c3ae2532c8e3f56612d15accba7a7a10c43b58294d77d4298fe1a25f8ca292079f1e1c31883911aecdcd9a2a562395ce8d38e31cf26ff2eee4d58ef14deba4266cbce823d618c2631d46e6e81ae1fbf7148eeb9a53e51b560923b07282b029f1ff228ca6a33bcd5f9d5107de7d094bdf4391674d7f0cd5d33e2ed78ee0f969339164b87295ba5251c51c0bab383cd99003e28dcafa4ea12c9f5288e8a08fe9552717c46362eb79a0dfe18333e2bf555699c4c9a65dc39303262d4eaa03f715b623e0bee87091c619de808ae621d2bd4e5f6add6d7556928935291995b7a7a6c45,publicExponent=10001} 2025-09-01 12:04:29.625 21339-21573 DecryptUtil com.kotei.overseas.navi I 创建密钥和IV规格 success! 2025-09-01 12:04:29.627 21339-21573 DecryptUtil com.kotei.overseas.navi I 创建Cipher实例并初始化 success! 2025-09-01 12:04:30.034 21339-21573 i.overseas.navi com.kotei.overseas.navi I Waiting for a blocking GC Alloc 2025-09-01 12:04:30.047 21339-21346 i.overseas.navi com.kotei.overseas.navi I Background concurrent copying GC freed 97KB AllocSpace bytes, 13(130MB) LOS objects, 11% free, 376MB/424MB, paused 299us,45us total 125.900ms 2025-09-01 12:04:30.047 21339-21573 i.overseas.navi com.kotei.overseas.navi I WaitForGcToComplete blocked Alloc on Background for 13.142ms 2025-09-01 12:04:48.511 21339-21346 i.overseas.navi com.kotei.overseas.navi I Background concurrent copying GC freed 32KB AllocSpace bytes, 6(60MB) LOS objects, 12% free, 336MB/384MB, paused 7.461ms,30us total 40.617ms 2025-09-01 12:04:59.870 21339-21573 DecryptUtil com.kotei.overseas.navi I 解密并写入数据 success! 2025-09-01 12:05:00.803 21339-21573 DecryptUtil com.kotei.overseas.navi I dataVerification success 2025-09-01 12:05:05.628 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:05:06.003 21339-21346 i.overseas.navi com.kotei.overseas.navi I Background concurrent copying GC freed 658KB AllocSpace bytes, 406(52MB) LOS objects, 13% free, 314MB/362MB, paused 127us,35us total 109.342ms 2025-09-01 12:05:09.994 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:05:10.307 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:05:15.019 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:06:05.754 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:06:10.017 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:06:10.320 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:06:14.019 21339-21339 VRI[MainAc...y]@44606a7 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 12:06:18.075 21339-21573 DecryptUtil com.kotei.overseas.navi I inputVerifyDataPath/data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_3.zip 2025-09-01 12:06:18.076 21339-21573 DecryptUtil com.kotei.overseas.navi I inputVerifyDataPath[/data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_3.zip] -> signPath[/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/] 2025-09-01 12:06:18.153 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 338B/3.0GB progress:51.4 2025-09-01 12:06:19.109 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 9.9MB/3.0GB progress:51.42 2025-09-01 12:06:20.089 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 24.4MB/3.0GB progress:51.45 2025-09-01 12:06:21.090 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 38.9MB/3.0GB progress:51.48 2025-09-01 12:06:22.092 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 50.4MB/3.0GB progress:51.5 2025-09-01 12:06:23.092 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 63.3MB/3.0GB progress:51.53 2025-09-01 12:06:24.097 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 77.0MB/3.0GB progress:51.56 2025-09-01 12:06:25.097 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 88.3MB/3.0GB progress:51.58 2025-09-01 12:06:26.098 21339-21372 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 102.1MB/3.0GB progress:51.61
09-02
Code 分享 Notebook 保存成功 Python 3 (ipykernel) import os import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.preprocessing import MinMaxScaler from tensorflow.keras import layers,losses,optimizers, Sequential from tensorflow.keras.layers import InputLayer, Dense, LSTM, Dropout from tensorflow.keras.models import load_model from tensorflow.keras import Model 2秒 2025-06-21 17:14:21.232222: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`. 2025-06-21 17:14:21.246790: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered WARNING: All log messages before absl::InitializeLog() is called are written to STDERR E0000 00:00:1750497261.261784 789 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered E0000 00:00:1750497261.266275 789 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered W0000 00:00:1750497261.278129 789 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once. W0000 00:00:1750497261.278144 789 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once. W0000 00:00:1750497261.278145 789 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once. W0000 00:00:1750497261.278147 789 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once. 2025-06-21 17:14:21.282294: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations. To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags. + Code + Markdown pip install tensorflow 26秒 Looking in indexes: https://mirrors.cloud.aliyuncs.com/pypi/simple Collecting tensorflow Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/ba/1c/370b5546cf7afc29649b2fb74c171ef2493a36f62cf901c1425ead4a56af/tensorflow-2.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (644.9 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 644.9/644.9 MB 8.5 MB/s eta 0:00:0000:0100:01 Requirement already satisfied: absl-py>=1.0.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (2.3.0) Collecting astunparse>=1.6.0 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/2b/03/13dde6512ad7b4557eb792fbcf0c653af6076b81e5941d36ec61f7ce6028/astunparse-1.6.3-py2.py3-none-any.whl (12 kB) Requirement already satisfied: flatbuffers>=24.3.25 in /usr/local/lib/python3.11/site-packages (from tensorflow) (25.2.10) Collecting gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/a3/61/8001b38461d751cd1a0c3a6ae84346796a5758123f3ed97a1b121dfbf4f3/gast-0.6.0-py3-none-any.whl (21 kB) Collecting google-pasta>=0.1.1 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/a3/de/c648ef6835192e6e2cc03f40b19eeda4382c49b5bafb43d88b931c4c74ac/google_pasta-0.2.0-py3-none-any.whl (57 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.5/57.5 kB 11.0 MB/s eta 0:00:00 Collecting libclang>=13.0.0 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/1d/fc/716c1e62e512ef1c160e7984a73a5fc7df45166f2ff3f254e71c58076f7c/libclang-18.1.1-py2.py3-none-manylinux2010_x86_64.whl (24.5 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 24.5/24.5 MB 121.7 MB/s eta 0:00:0000:0100:01 Collecting opt-einsum>=2.3.2 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl (71 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 71.9/71.9 kB 16.5 MB/s eta 0:00:00 Requirement already satisfied: packaging in /usr/local/lib/python3.11/site-packages (from tensorflow) (24.2) Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<6.0.0dev,>=3.20.3 in /usr/local/lib/python3.11/site-packages (from tensorflow) (3.20.3) Requirement already satisfied: requests<3,>=2.21.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (2.32.3) Requirement already satisfied: setuptools in /usr/local/lib/python3.11/site-packages (from tensorflow) (65.5.1) Requirement already satisfied: six>=1.12.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (1.17.0) Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (3.1.0) Requirement already satisfied: typing-extensions>=3.6.6 in /usr/local/lib/python3.11/site-packages (from tensorflow) (4.12.2) Requirement already satisfied: wrapt>=1.11.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (1.17.2) Requirement already satisfied: grpcio<2.0,>=1.24.3 in /usr/local/lib/python3.11/site-packages (from tensorflow) (1.72.1) Requirement already satisfied: tensorboard~=2.19.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (2.19.0) Collecting keras>=3.5.0 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/95/e6/4179c461a5fc43e3736880f64dbdc9b1a5349649f0ae32ded927c0e3a227/keras-3.10.0-py3-none-any.whl (1.4 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 110.8 MB/s eta 0:00:00 Requirement already satisfied: numpy<2.2.0,>=1.26.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (1.26.4) Requirement already satisfied: h5py>=3.11.0 in /usr/local/lib/python3.11/site-packages (from tensorflow) (3.13.0) Collecting ml-dtypes<1.0.0,>=0.5.1 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/cc/2a/5421fd3dbe6eef9b844cc9d05f568b9fb568503a2e51cb1eb4443d9fc56b/ml_dtypes-0.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.7 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.7/4.7 MB 144.7 MB/s eta 0:00:00 Collecting tensorflow-io-gcs-filesystem>=0.23.1 (from tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/66/7f/e36ae148c2f03d61ca1bff24bc13a0fef6d6825c966abef73fc6f880a23b/tensorflow_io_gcs_filesystem-0.37.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.1 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.1/5.1 MB 158.1 MB/s eta 0:00:00 Requirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.11/site-packages (from astunparse>=1.6.0->tensorflow) (0.45.1) Requirement already satisfied: rich in /usr/local/lib/python3.11/site-packages (from keras>=3.5.0->tensorflow) (13.9.4) Collecting namex (from keras>=3.5.0->tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/b2/bc/465daf1de06409cdd4532082806770ee0d8d7df434da79c76564d0f69741/namex-0.1.0-py3-none-any.whl (5.9 kB) Collecting optree (from keras>=3.5.0->tensorflow) Downloading https://mirrors.cloud.aliyuncs.com/pypi/packages/43/6e/3721bf455834a4cfef1ecd9410666ec1d5708b32f01f57da7c10c2297e09/optree-0.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (416 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 416.8/416.8 kB 69.7 MB/s eta 0:00:00 Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/site-packages (from requests<3,>=2.21.0->tensorflow) (3.4.1) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests<3,>=2.21.0->tensorflow) (3.10) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/site-packages (from requests<3,>=2.21.0->tensorflow) (2.3.0) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/site-packages (from requests<3,>=2.21.0->tensorflow) (2025.1.31) Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.11/site-packages (from tensorboard~=2.19.0->tensorflow) (3.8) Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.11/site-packages (from tensorboard~=2.19.0->tensorflow) (0.7.2) Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.11/site-packages (from tensorboard~=2.19.0->tensorflow) (3.1.3) Requirement already satisfied: MarkupSafe>=2.1.1 in /usr/local/lib/python3.11/site-packages (from werkzeug>=1.0.1->tensorboard~=2.19.0->tensorflow) (3.0.2) Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.11/site-packages (from rich->keras>=3.5.0->tensorflow) (3.0.0) Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.11/site-packages (from rich->keras>=3.5.0->tensorflow) (2.19.1) Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.11/site-packages (from markdown-it-py>=2.2.0->rich->keras>=3.5.0->tensorflow) (0.1.2) DEPRECATION: pytorch-lightning 1.7.7 has a non-standard dependency specifier torch>=1.9.*. pip 24.0 will enforce this behaviour change. A possible replacement is to upgrade to a newer version of pytorch-lightning or contact the author to suggest that they release a version with a conforming dependency specifiers. Discussion can be found at https://github.com/pypa/pip/issues/12063 Installing collected packages: namex, libclang, tensorflow-io-gcs-filesystem, optree, opt-einsum, ml-dtypes, google-pasta, gast, astunparse, keras, tensorflow Successfully installed astunparse-1.6.3 gast-0.6.0 google-pasta-0.2.0 keras-3.10.0 libclang-18.1.1 ml-dtypes-0.5.1 namex-0.1.0 opt-einsum-3.4.0 optree-0.16.0 tensorflow-2.19.0 tensorflow-io-gcs-filesystem-0.37.1 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv [notice] A new release of pip is available: 23.3.2 -> 25.1.1 [notice] To update, run: pip install --upgrade pip Note: you may need to restart the kernel to use updated packages. + Code + Markdown stock_data = pd.read_csv('IBM_stock_data.csv') 0秒 + Code + Markdown stock_data.head() 0秒 date Open High Low Close Volume Price Change % 0 1999/11/1 98.50 98.81 96.37 96.75 9551800 0.000000 1 1999/11/2 96.75 96.81 93.69 94.81 11105400 -2.005168 2 1999/11/3 95.87 95.94 93.50 94.37 10369100 -0.464086 3 1999/11/4 94.44 94.44 90.00 91.56 16697600 -2.977641 4 1999/11/5 92.75 92.94 90.19 90.25 13737600 -1.430756 + Code + Markdown scaler = MinMaxScaler(feature_range=(0, 1)) scaled_data = scaler.fit_transform(stock_data['Close'].values.reshape(-1, 1)) 0秒 + Code + Markdown scaled_data[0] 0秒 array([0.23131139]) + Code + Markdown len(scaled_data) 0秒 6293 + Code + Markdown training_data_len = int(np.ceil(len(scaled_data) * 0.8)) #向上取整 0秒 + Code + Markdown train_data = scaled_data[0:training_data_len] X_train, y_train = [], [] time_step = 10 # 时间窗口,模型基于前10个时间步长进行预测。可以尝试不同长度(如20、30)并观察效果变化。 0秒 + Code + Markdown for i in range(len(train_data) - time_step - 1): X_train.append(train_data[i:(i + time_step), 0]) y_train.append(train_data[i + time_step, 0]) 0秒 + Code + Markdown X_train, y_train = np.array(X_train), np.array(y_train) X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], 1) 0秒 + Code + Markdown X_train.shape 0秒 (5024, 10, 1) + Code + Markdown X_test, y_test = [], [] test_data = scaled_data[training_data_len - time_step:] ​ for i in range(len(test_data) - time_step): X_test.append(test_data[i:(i + time_step), 0]) y_test.append(test_data[i + time_step, 0]) ​ X_test, y_test = np.array(X_test), np.array(y_test) X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], 1) 0秒 + Code + Markdown model = Sequential() model.add(InputLayer(input_shape=(X_train.shape[1], 1))) model.add(LSTM(units=64, return_sequences=True)) # 调整Dropout: 当前设置为0.3,可以尝试在不同的层上使用不同的Dropout值,例如0.2~0.5之间。Dropout的作用是防止过拟合。 model.add(Dropout(0.3)) model.add(LSTM(units=64, return_sequences=True)) model.add(Dropout(0.3)) model.add(LSTM(units=32)) model.add(Dropout(0.2)) # 增加回归层: 如果希望更高的拟合精度,可以添加多个Dense层,例如在输出前再增加一层Dense。 model.add(Dense(units=1)) 0秒 /usr/local/lib/python3.11/site-packages/keras/src/layers/core/input_layer.py:27: UserWarning: Argument `input_shape` is deprecated. Use `shape` instead. warnings.warn( 2025-06-21 17:14:34.072049: E external/local_xla/xla/stream_executor/cuda/cuda_platform.cc:51] failed call to cuInit: INTERNAL: CUDA error: Failed call to cuInit: UNKNOWN ERROR (303) + Code + Markdown def MyRNN(): model = Sequential([ layers.InputLayer(input_shape=(X_train.shape[1],1)), layers.SimpleRNN(units=64, dropout=0.5, return_sequences=True, unroll=True), layers.SimpleRNN(units=64, dropout=0.5, unroll=True), layers.Dense(1)] ) return model 0秒 + Code + Markdown model = MyRNN() model.compile(optimizer='adam', loss='mean_squared_error') 0秒 + Code + Markdown history = model.fit( X_train, y_train, epochs=2, # 批大小(batch_size): 尝试不同的batch_size,例如16、32、64,以找到训练稳定性和准确性之间的平衡 batch_size=32, #callbacks=[early_stopping, lr_scheduler] ) 2秒 Epoch 1/2 157/157 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - loss: 0.1122 Epoch 2/2 157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0278 + Code + Markdown model.save('my_model.keras') 0秒 + Code + Markdown train_loss = model.evaluate(X_train, y_train, verbose=0) test_loss = model.evaluate(X_test, y_test, verbose=0) 1秒 + Code + Markdown # 训练集上计算的损失值,数值越小表示模型在训练数据上的拟合效果越好 print(f"Training Loss: {train_loss:.4f}") # 测试集上计算的损失值,反映了模型在未见过的数据上的表现。测试损失略高于训练损失,但差距不大,说明模型在新数据上的表现依然良好。 print(f"Testing Loss: {test_loss:.4f}") 0秒 Training Loss: 0.0459 Testing Loss: 0.0538 + Code + Markdown model = load_model('my_model.keras') predictions = model.predict(X_test) predictions = scaler.inverse_transform(predictions) # 反归一化预测值 0秒 40/40 ━━━━━━━━━━━━━━━━━━━━ 1s 8ms/step + Code + Markdown Code # 将数据分为训练集和验证集 train = stock_data[:training_data_len] valid = stock_data[training_data_len:] valid.loc[:, 'Predictions'] = predictions # 将预测结果添加到验证集的 DataFrame 中 ​ # 绘制图像 plt.figure(figsize=(14, 5)) plt.title('Stock Price Prediction', fontsize=20) # 图表标题改为英文 plt.xlabel('Date', fontsize=14) # X 轴标签改为英文 plt.ylabel('Close Price', fontsize=14) # Y 轴标签改为英文 plt.plot(train['date'], train['Close'], label='Training Data', color='blue') # 训练数据标签改为英文 plt.plot(valid['date'], valid['Close'], label='Actual Price', color='green') # 真实价格标签改为英文 plt.plot(valid['date'], valid['Predictions'], label='Predicted Price', color='red') # 预测价格标签改为英文 plt.legend() # 添加图例 # 添加保存图像的代码 plt.savefig('stock_price_predictions.png') # 保存图像 plt.show() ​ # 计算和输出评估指标 rmse = np.sqrt(np.mean(np.square(predictions - y_test))) # 计算均方根误差 mae = np.mean(np.abs(predictions - y_test)) # 计算平均绝对误差 print(f'RMSE (Root Mean Square Error): {rmse}, MAE (Mean Absolute Error): {mae}') # 输出信息改为英文 34秒 /tmp/ipykernel_789/422633826.py:4: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy valid.loc[:, 'Predictions'] = predictions # 将预测结果添加到验证集的 DataFrame 中 RMSE (Root Mean Square Error): 102.69734326363911, MAE (Mean Absolute Error): 101.96265451052737误差好大在此代码基础上改进
06-22
11:38:01.885 19493-19787 DecryptUtil com.kotei.overseas.navi I 原始 ZIP 文件已成功删除: /data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_2.zip 2025-09-01 11:38:01.885 19493-19787 DecryptUtil com.kotei.overseas.navi I aesDaraPath:/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/KVM_Navi_EU_148_2.zip.enc 2025-09-01 11:38:01.886 19493-19787 DecryptUtil com.kotei.overseas.navi I signDataPtah/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/KVM_Navi_EU_148_2.zip.enc.sig 2025-09-01 11:38:02.566 19493-19493 VRI[MainAc...y]@fe06fc1 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 11:38:06.810 19493-19787 DecryptUtil com.kotei.overseas.navi I base64格式签名数据: g013oFHKCv0K+0I5nwbCToUOch9aIfSNLhgyDzJ30uM5gN8VlS/K3l5gAHtjMv/Lm0269M5vtAdsi/tJ3sPdFSkZrdyfxA2CFaxAZpOMuLe7AJEZfbvRX0+XY88lArYvivdfK/6gTGdGYiTSsFzvzxYvSd/yGmmJOZKP9zmAVySAhyTcAbqenxV4nIfefD9t3R/OSxlKqCJsKZiO800ffXO010s65U64XVqh0Fc+2I0PibzA2LyYJ2q+YObkABe9W2YDgXWq91bzPNtX+nSRV4T6773Mf+i4j2MedPIb7LwFwx5HD3eFUpmp+KY5jYFUO4anW5UemQDwJne/Xurstw== 2025-09-01 11:38:06.817 19493-19787 DecryptUtil com.kotei.overseas.navi I PUBLIC_KEY: OpenSSLRSAPublicKey{modulus=cb4a3563eed96eff8d354083c7e2cc066aacc8f1aca2343a3ed5f9d271c41c9c3ae2532c8e3f56612d15accba7a7a10c43b58294d77d4298fe1a25f8ca292079f1e1c31883911aecdcd9a2a562395ce8d38e31cf26ff2eee4d58ef14deba4266cbce823d618c2631d46e6e81ae1fbf7148eeb9a53e51b560923b07282b029f1ff228ca6a33bcd5f9d5107de7d094bdf4391674d7f0cd5d33e2ed78ee0f969339164b87295ba5251c51c0bab383cd99003e28dcafa4ea12c9f5288e8a08fe9552717c46362eb79a0dfe18333e2bf555699c4c9a65dc39303262d4eaa03f715b623e0bee87091c619de808ae621d2bd4e5f6add6d7556928935291995b7a7a6c45,publicExponent=10001} 2025-09-01 11:38:06.818 19493-19787 DecryptUtil com.kotei.overseas.navi I 创建密钥和IV规格 success! 2025-09-01 11:38:06.819 19493-19787 DecryptUtil com.kotei.overseas.navi I 创建Cipher实例并初始化 success! 2025-09-01 11:38:37.028 19493-19787 DecryptUtil com.kotei.overseas.navi I 解密并写入数据 success! 2025-09-01 11:38:37.835 19493-19787 DecryptUtil com.kotei.overseas.navi I dataVerification success 2025-09-01 11:38:46.831 19493-19506 i.overseas.navi com.kotei.overseas.navi I Background concurrent copying GC freed 570KB AllocSpace bytes, 386(49MB) LOS objects, 13% free, 308MB/356MB, paused 6.418ms,41us total 41.373ms 2025-09-01 11:38:51.451 19493-19493 VRI[MainAc...y]@fe06fc1 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 11:38:55.600 19493-19493 VRI[MainAc...y]@fe06fc1 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 11:38:56.288 19493-19493 VRI[MainAc...y]@fe06fc1 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 11:39:02.605 19493-19493 VRI[MainAc...y]@fe06fc1 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 11:39:03.923 19493-19932 i.overseas.navi com.kotei.overseas.navi W Long monitor contention with owner pool-8-thread-3 (19931) at int java.util.zip.ZipFile$ZipFileInputStream.read(byte[], int, int)(ZipFile.java:1010) waiters=3 in int java.util.zip.ZipFile$ZipFileInputStream.read(byte[], int, int) for 119ms 2025-09-01 11:39:51.253 19493-19493 VRI[MainAc...y]@fe06fc1 com.kotei.overseas.navi I onDisplayChanged oldDisplayState=2 newDisplayState=2 2025-09-01 11:39:54.596 19493-19787 DecryptUtil com.kotei.overseas.navi I inputVerifyDataPath/data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_3.zip 2025-09-01 11:39:54.596 19493-19787 DecryptUtil com.kotei.overseas.navi I inputVerifyDataPath[/data/user/0/com.kotei.overseas.navi/cache/KVM_Navi_EU_148_3.zip] -> signPath[/storage/emulated/0/Android/data/com.kotei.overseas.navi/files/overseas/data/mapoffline/sign/] 2025-09-01 11:39:54.686 19493-19533 USBOfflineUpdater com.kotei.overseas.navi I message: 解密验签 KVM_Navi_EU_148_3.zip: 338B/3.0GB progress:51.4 目前跳过了第二个zip包的处理导致进度十分异常:package com.kotei.overseas.navi.update; import static com.kotei.overseas.navi.security.DecryptUtil.dataVerification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; import android.util.Log; import androidx.annotation.NonNull; import com.here.sdk.core.engine.SDKNativeEngine; import com.here.sdk.maploader.MapDownloader; import com.here.sdk.maploader.MapDownloaderConstructionCallback; import com.kotei.overseas.navi.business.data.MapDataController; import com.kotei.overseas.navi.security.DecryptUtil; import com.kotei.overseas.navi.security.DfCert; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import java.util.stream.Stream; /** * USB离线更新系统 */ public class USBOfflineUpdater { private static final String TAG = "USBOfflineUpdater"; // 状态码定义 /** * 操作成功 */ public static final int SUCCESS = 0; /** * 错误:未检测到USB设备 */ public static final int ERROR_NO_USB = 1; /** * 错误:未找到升级包文件 */ public static final int ERROR_NO_UPDATE_PACKAGE = 2; /** * 错误:电池电量不足(低于安全阈值) */ public static final int ERROR_BATTERY_LOW = 3; /** * 错误:存储空间不足 */ public static final int ERROR_STORAGE_INSUFFICIENT = 4; /** * 错误:系统正在执行其他升级任务 */ public static final int ERROR_UPDATE_IN_PROGRESS = 5; /** * 错误:文件复制失败(检查存储权限或磁盘状态) */ public static final int ERROR_COPY_FAILED = 6; /** * 错误:升级包解压失败(文件可能损坏) */ public static final int ERROR_EXTRACT_FAILED = 7; /** * 错误:用户手动取消操作 */ public static final int ERROR_USER_CANCELED = 8; /** * 错误:未预期的系统异常 */ public static final int ERROR_UNEXPECTED = 9; /** * 错误:升级过程中USB设备被移除 */ public static final int ERROR_USB_REMOVED = 10; /** * 错误:车辆档位未处于停车挡(P档) */ public static final int ERROR_VEHICLE_SHIFTED = 11; /** * 错误:电池电量极低(无法维持升级过程) */ public static final int ERROR_BATTERY_TOO_LOW = 12; /** * 错误:文件校验失败(MD5/SHA256校验不匹配) */ public static final int ERROR_FILE_VERIFY_FAILED = 13; /** * 错误:文件解密或验签失败 */ public static final int ERROR_DECRYPT_OR_SIGN_FAILED = 14; // 更新阶段定义 /** * 空闲状态(未开始升级) */ private static final int PHASE_IDLE = 0; /** * 设备检测阶段(检查USB/存储设备) */ private static final int PHASE_DETECTING = 1; /** * 升级包校验阶段(验证完整性/签名) */ private static final int PHASE_CHECKING = 2; /** * 系统备份阶段(备份当前系统数据) */ private static final int PHASE_BACKUP = 3; /** * 文件复制阶段(写入升级包到临时分区) */ private static final int PHASE_COPYING = 4; /** * 解压阶段(解压升级包内容) */ private static final int PHASE_EXTRACTING = 5; /** * 清理阶段(删除临时文件) */ private static final int PHASE_CLEANUP = 6; /** * 回滚阶段(升级失败时恢复备份) */ private static final int PHASE_ROLLBACK = 7; // 权重分配比例 private static final float BACKUP_WEIGHT = 0.1f; // 备份阶段权重10% private static final float PACKAGE_COPY_WEIGHT = 0.29f; // 升级包拷贝阶段权重29% private static final float PACKAGE_VERIFY_WEIGHT = 0.31f; // 升级包解密验签阶段权重31% private static final float PACKAGE_EXTRACT_WEIGHT = 0.29f; // 升级包解压阶段权重29% private static final float VERIFICATION_WEIGHT = 0.01f; // 校验阶段权重1% // 声明 mProgress 为实例变量(非静态) private float mProgress = 0; // 当前进度值(0~1) private static USBOfflineUpdater instance; private final Context context; private UpdateTask currentTask; private UpdateListener updateListener; private SDKNativeEngine sdkNativeEngine; private MapDataController mapDataController; // 目录配置 private File usbRoot; private File cacheDir; private File storageDir;// private File backupDir; // // 更新控制 public boolean isPaused = false; public boolean isCancelled = false; public final AtomicInteger currentPhase = new AtomicInteger(PHASE_IDLE); // 错误信息 private String lastErrorMessage = ""; // 电量阈值 private static final int MIN_BATTERY_LEVEL = 30; // 最低电量百分比 private static final int MIN_BATTERY_LEVEL_CRITICAL = 15; // 严重低电量 //升级包格式 private static final String FILE_NAME_PATTERN = "^KVM_Navi_EU_" + "(?<version>\\d{1,3})" // 版本号(1-3位数字) + "_" + "(?<serial>\\d{1,2})" // 1-2位数字编号 + "(\\.\\w+)?$"; // 可选的文件扩展名 // 进度计算相关变量(新增) private long totalUpdateSize = 0; // 所有升级包总大小 private long UpdateSize = 0; // 当前升级包大小 private long currentCopiedBytes = 0; // 当前已拷贝字节数 private long currentVerifiedBytes = 0; // 当前已验签字节数 private long currentExtractedBytes = 0; // 当前已解压字节数 private long backupSize = 0; // 备份数据大小 private long storageSize = 0; private long rollBackCopiedBytes = 0; private int currentPackageIndex = 0; // 当前处理的升级包索引 private int totalPackageCount = 0; // 总升级包数量 // 用于跟踪回滚状态 public boolean isRollingBack = false; private long rollbackTotalSize = 0; private long rollbackProcessedSize = 0; //zwx取消逻辑完善对应:P1:备份未完成点击取消-程序死机 private boolean isBackupCompleted = false; // USB监听器 private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) { //zwxtest // File usbPath = new File(intent.getData().getPath()); File usbPath = new File("/data/data/com.kotei.overseas.navi/files"); if (usbPath.exists() && usbPath.canRead()) { usbRoot = usbPath; Log.i(TAG, "USB mounted: " + usbRoot.getAbsolutePath()); } } else if (Intent.ACTION_MEDIA_EJECT.equals(action) || Intent.ACTION_MEDIA_UNMOUNTED.equals(action)) { if (currentTask != null && currentPhase.get() > PHASE_CHECKING) { cancelUpdate(ERROR_USB_REMOVED, "USB设备被移除"); } usbRoot = null; Log.e(TAG, "USB removed"); } } }; // // 车辆状态监听器(模拟) private final BroadcastReceiver vehicleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if ("com.example.ACTION_SHIFT_CHANGE".equals(intent.getAction())) { String shift = intent.getStringExtra("shift"); if (!"P".equals(shift) && currentPhase.get() > PHASE_CHECKING) { // cancelUpdate(ERROR_VEHICLE_SHIFTED, "车辆已退出P挡"); } } } }; // 单例模式 public static synchronized USBOfflineUpdater getInstance(Context context) { if (instance == null) { instance = new USBOfflineUpdater(context); } return instance; } public static synchronized USBOfflineUpdater getInstance() { return instance; } private USBOfflineUpdater(Context context) { this.context = context.getApplicationContext(); // 初始化SDK sdkNativeEngine = SDKNativeEngine.getSharedInstance(); mapDataController = MapDataController.getInstance(); try { DfCert.getInstance().getService(); } catch (Exception e) { Log.e(TAG, "Exception:" + e.toString()); } //zwx 执行顺序? // 初始化目录(默认值) Log.i(TAG, "zwx:=== 启动目录初始化 ===" ); cacheDir = this.context.getCacheDir(); Log.i(TAG, "初始化cacheDir目录为:" + cacheDir.getAbsolutePath());//ZWX storageDir = new File(sdkNativeEngine.getOptions().persistentMapStoragePath); Log.i(TAG, "初始化storageDir目录为:" + storageDir.getAbsolutePath());//ZWX // ✅ 初始化 backupDir backupDir = new File(cacheDir, "backup"); // ✅ 基于 cacheDir 固定路径 // 注册USB监听器 IntentFilter usbFilter = new IntentFilter(); usbFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); usbFilter.addAction(Intent.ACTION_MEDIA_EJECT); usbFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); usbFilter.addDataScheme("file"); context.registerReceiver(usbReceiver, usbFilter); // 注册车辆状态监听器(模拟) IntentFilter vehicleFilter = new IntentFilter("com.example.ACTION_SHIFT_CHANGE"); context.registerReceiver(vehicleReceiver, vehicleFilter); //清除数据存储目录下的预留数据 removeLegacy(); } public void initialization(UpdateListener listener) { // isRollingBack = true; Log.i(TAG, "挂起线程执行初始化" );//ZWX this.updateListener = listener; Thread USBOfflineUpdaterInitialization = new Thread(new Runnable() { @Override public void run() { // 启动时检查恢复 checkRecoveryOnStartup(); } }); USBOfflineUpdaterInitialization.setName("USBOfflineUpdaterInitialization"); USBOfflineUpdaterInitialization.start(); } // 动态设置目录 public void setDirectories(File usbRoot, File cacheDir, File storageDir) { if (usbRoot != null) { this.usbRoot = usbRoot; } if (cacheDir != null) { this.cacheDir = cacheDir; } if (storageDir != null) { this.storageDir = storageDir; } } /** * 检测升级包 * * @return 状态码 (SUCCESS 或错误码) */ public int detectUpdatePackages() { //zwxtest // 1. 检测USB是否插入 File usbPath = new File("/data/data/com.kotei.overseas.navi/files"); if (usbPath.exists() && usbPath.canRead()) { usbRoot = usbPath; Log.i(TAG, "USB mounted: " + usbRoot.getAbsolutePath()); } //zwxtest if (usbRoot == null || !usbRoot.exists() || !usbRoot.isDirectory()) { return ERROR_NO_USB; } File[] tempPackages = usbRoot.listFiles(); // 2. 查找升级包 (命名格式: update_v{版本号}_{日期}.zip) File[] packages = usbRoot.listFiles(file -> file.isFile() && file.getName().matches(FILE_NAME_PATTERN) ); return (packages != null && packages.length > 0) ? SUCCESS : ERROR_NO_UPDATE_PACKAGE; } /** * 环境检测 * * @return 状态码 (SUCCESS 或错误码) */ public int checkEnvironment() { // 1. 检测电量 int batteryLevel = PowerUtils.getBatteryLevel(context); if (batteryLevel < MIN_BATTERY_LEVEL) { return batteryLevel < MIN_BATTERY_LEVEL_CRITICAL ? ERROR_BATTERY_TOO_LOW : ERROR_BATTERY_LOW; } // 2. 检测缓存空间 (需大于15GB) long requiredSpace = 15L * 1024 * 1024 * 1024; // 15GB long availableSpace = StorageUtils.getAvailableSpace(cacheDir); if (availableSpace < requiredSpace) { Log.e(TAG, "缓存空间剩余:【" + availableSpace + "】"); return ERROR_STORAGE_INSUFFICIENT; } return SUCCESS; } /** * 判读是否正在进行离线更新 */ public boolean isOfflineUpdate() { return currentTask != null && !currentTask.isCancelled(); } /** * 用户点击USB更新-点击查看-触发startUpdate-触发startUpdate重载(本方法)-调用UpdateTask */ public void startUpdate(UpdateListener listener) { this.updateListener = listener; int result = checkEnvironment(); if (result != SUCCESS) { notifyListener(result, "环境检测不合格"); return; } if (isRollingBack) { notifyListener(ERROR_UPDATE_IN_PROGRESS, "正在进行数据回滚"); return; } if (isOfflineUpdate()) { notifyListener(ERROR_UPDATE_IN_PROGRESS, "已有更新任务正在进行"); return; } Log.i(TAG, "检测到更新任务触发,开始进行地图更新"); notifyProgress("开始进行更新"); // 计算总工作量(新增) calculateTotalWorkload(); currentTask = new UpdateTask(); currentTask.execute(); } // 计算总工作量(新增) private void calculateTotalWorkload() { totalUpdateSize = 0; File[] packages = getUpdatePackages(); totalPackageCount = packages != null ? packages.length : 0; if (packages != null) { for (File pkg : packages) { totalUpdateSize += pkg.length(); } } // backupSize = estimateBackupSize();//zwx backupSize = FileUtils.getDirectorySize(storageDir); Log.i(TAG, "总工作量计算: 升级包数量=" + totalPackageCount + ", 升级包大小=" + formatSize(totalUpdateSize) + ", 备份大小=" + formatSize(backupSize)); } // 获取更新包(新增) private File[] getUpdatePackages() { if (usbRoot == null) return new File[0]; return usbRoot.listFiles(file -> file.isFile() && file.getName().matches(FILE_NAME_PATTERN) ); } // 格式化文件大小(新增) private String formatSize(long size) { if (size < 1024) return size + "B"; else if (size < 1024 * 1024) return String.format("%.1fKB", size / 1024.0); else if (size < 1024 * 1024 * 1024) return String.format("%.1fMB", size / (1024.0 * 1024)); else return String.format("%.1fGB", size / (1024.0 * 1024 * 1024)); } /** * 暂停更新 */ public void pauseUpdate() { isPaused = true; notifyProgress("更新已暂停"); } /** * 恢复更新 */ public void resumeUpdate() { isPaused = false; notifyProgress("更新已恢复"); } /** * 取消更新 */ public void cancelUpdate() { cancelUpdate(ERROR_USER_CANCELED, "用户取消更新"); // performRollback(backupDir);//zwx } private void cancelUpdate(int errorCode, String message) { isCancelled = true; lastErrorMessage = message; notifyListener(errorCode, message); } // 进度通知 // private void notifyProgress(String message) { // new Handler(Looper.getMainLooper()).post(() -> { // if (updateListener != null) { // // 计算当前总进度(修改) // float progress = calculateOverallProgress(); // updateListener.onProgress(currentPhase.get(), progress, message); // } // }); // } //zwx更改对应:P4: 升级包的解压阶段,进度会跳变,未能成功在UI显示进度 private final Handler uiHandler = new Handler(Looper.getMainLooper()); private void notifyProgress(String message) { uiHandler.post(() -> { if (updateListener != null) { float progress = calculateOverallProgress(); updateListener.onProgress(currentPhase.get(), progress, message); } }); } private float calculateOverallProgress() { // if (isRollingBack) { // // 回滚阶段:直接计算回滚进度 // if (rollbackTotalSize > 0) { // mProgress = 99; // return mProgress; // } // return 0; // } if (totalPackageCount == 0 && currentPhase.get() != PHASE_ROLLBACK) return 0; // 每个包的总权重(拷贝+验签+解压) float packageTotalWeight = PACKAGE_COPY_WEIGHT + PACKAGE_VERIFY_WEIGHT + PACKAGE_EXTRACT_WEIGHT; //再次确认totalPackageCount是否等于0 if (totalPackageCount == 0) { throw new IllegalStateException("totalPackageCount should not be 0 here!"); } // 每个包的阶段权重 float packageCopyWeight = PACKAGE_COPY_WEIGHT / totalPackageCount; float packageVerifyWeight = PACKAGE_VERIFY_WEIGHT / totalPackageCount; float packageExtractWeight = PACKAGE_EXTRACT_WEIGHT / totalPackageCount; switch (currentPhase.get()) { case PHASE_BACKUP: if(backupSize > 0) { mProgress = BACKUP_WEIGHT * (currentCopiedBytes / (float) backupSize); }else{ mProgress = BACKUP_WEIGHT; } if(mProgress > BACKUP_WEIGHT) { mProgress = BACKUP_WEIGHT; } break; case PHASE_COPYING: // 基础:备份 + 已完成包的完整进度 float copyBase = BACKUP_WEIGHT + (packageTotalWeight * currentPackageIndex) / totalPackageCount; // 增量:当前包拷贝进度 float copyProgress = currentCopiedBytes / (float) UpdateSize; mProgress = copyBase + packageCopyWeight * copyProgress; break; case PHASE_CHECKING: // 基础:备份 + 已完成包的完整进度 + 当前包拷贝完成 float verifyBase = BACKUP_WEIGHT + (packageTotalWeight * currentPackageIndex) / totalPackageCount + packageCopyWeight; // 增量:当前包验签进度 float verifyProgress = currentVerifiedBytes / (float) UpdateSize; mProgress = verifyBase + packageVerifyWeight * verifyProgress; break; case PHASE_EXTRACTING: // 修复:添加当前包验签完成 float extractBase = BACKUP_WEIGHT + (packageTotalWeight * currentPackageIndex) / totalPackageCount + packageCopyWeight + packageVerifyWeight; // 添加这行 // 增量:当前包解压进度 float extractProgress = currentExtractedBytes / (float) UpdateSize; mProgress = extractBase + packageExtractWeight * extractProgress; break; case PHASE_DETECTING: mProgress = BACKUP_WEIGHT + packageTotalWeight + VERIFICATION_WEIGHT * (currentVerifiedBytes / (float) totalUpdateSize); break; case PHASE_CLEANUP: // case PHASE_ROLLBACK: // mProgress = 0.99f; // // break; case PHASE_ROLLBACK: // 增量:当前包拷贝进度 mProgress = rollBackCopiedBytes / (float) backupSize; break; } return Math.min(Math.round(mProgress * 10000) / 100.00f, 100.00f); } // 结果通知 private void notifyListener(int resultCode, String message) { new Handler(Looper.getMainLooper()).post(() -> { if (updateListener != null) { updateListener.onResult(resultCode, message); } }); } // 获取当前进度百分比 private int getCurrentProgress() { // 此处可添加子任务进度计算 return (int) mProgress; } // ================== 核心更新逻辑 ================== private class UpdateTask extends AsyncTask<Void, Void, Integer> { // private File backupFile;//zwx-s private File[] updatePackages; @Override protected void onPreExecute() { currentPhase.set(PHASE_DETECTING); isCancelled = false; isPaused = false; currentCopiedBytes = 0; currentExtractedBytes = 0; mProgress = 0; // 重置进度为0 } @Override protected Integer doInBackground(Void... voids) { try { // 阶段1: 备份数据 currentPhase.set(PHASE_BACKUP); notifyProgress("开始备份数据..."); // backupDir = new File(cacheDir, "backup"); Log.i(TAG, "初始化备份目录为:" + backupDir.getAbsolutePath());//ZWX if (backupDir.exists()) { if (!FileUtils.deleteRecursive(backupDir)) { throw new IOException("删除备份文件失败: " + backupDir.getAbsolutePath()); } } if (!backupDir.mkdirs()) { throw new IOException("创建备份目录失败: " + backupDir.getAbsolutePath()); } // 计算实际备份大小 Log.i(TAG, "核对实际需要备份的数据大小"); // backupSize = estimateBackupSize(); backupSize = FileUtils.getDirectorySize(storageDir); Log.i(TAG, "需要备份的数据大小:[" + formatSize(backupSize) + "]"); Log.i(TAG, "storage目录为:" + storageDir.getAbsolutePath());//ZWX if (backupSize > 0) { Log.i(TAG, "开始进行数据备份"); try { // 执行目录复制 if (!singlecopydirectory.copyDirectoryWithProgress(storageDir, backupDir, (copied, total) -> { currentCopiedBytes = copied; notifyProgress("备份中: " + formatSize(copied) + "/" + formatSize(total)); })) { throw new IOException("备份失败: " + storageDir.getAbsolutePath() + " -> " + backupDir.getAbsolutePath()); } notifyProgress("数据备份完成"); } catch (Exception e) { Log.e(TAG, "备份过程中发生错误", e); // 可选:尝试二次备份或记录日志,这里直接抛出错误 return ERROR_COPY_FAILED; } } else { // 无数据需要备份,直接标记完成 Log.i(TAG, "无备份数据,直接进行数据更新"); currentCopiedBytes = 1; backupSize = 1; notifyProgress("无数据需要备份"); } //备份已经完成标志位 isBackupCompleted = true; Log.i(TAG, "开始删除storage"); FileUtils.deleteDirectoryContents(storageDir); // 检查是否被取消(场景1) if (isCancelled) { return ERROR_USER_CANCELED; } //zwx-end // 阶段2: 处理升级包 updatePackages = getUpdatePackages(); Log.i(TAG, "开始处理升级包【拷贝、解密验签、解压】"); for (currentPackageIndex = 0; currentPackageIndex < updatePackages.length; currentPackageIndex++) { if (isCancelled) return ERROR_USER_CANCELED; // 处理暂停状态 while (isPaused) { Thread.sleep(500); } File packageFile = updatePackages[currentPackageIndex]; UpdateSize = updatePackages[currentPackageIndex].length(); String packageName = packageFile.getName(); long packageSize = packageFile.length(); // 备份完成后重置拷贝计数器 currentCopiedBytes = 0; // 阶段3: 拷贝升级包 currentPhase.set(PHASE_COPYING); // File destFile = new File(storageDir, packageName); File destFile = new File(cacheDir, packageName);//zwx修改 // 拷贝时更新进度(修改) boolean copyResult = FileUtils.copyFileWithProgress( packageFile, destFile, (copied, total) -> { currentCopiedBytes = copied; notifyProgress(String.format("拷贝 %s: %s/%s", packageName, formatSize(copied), formatSize(total))); } ); if (!copyResult) { lastErrorMessage = "拷贝失败: " + packageName; return ERROR_COPY_FAILED; } // 阶段4:解密验签 currentPhase.set(PHASE_CHECKING); currentVerifiedBytes = 0; // 重置验签计数器 // 创建进度回调适配器 DecryptUtil.ProgressCallback decryptCallback = new DecryptUtil.ProgressCallback() { @Override public void onProgress(long processed, long total) { // 直接更新验签进度计数器 currentVerifiedBytes = processed; // 触发进度通知 notifyProgress(String.format("解密验签 %s: %s/%s", packageName, formatSize(processed), formatSize(total))); } }; // 执行解密验签(传入回调) if (!dataVerification(destFile.getAbsolutePath(), storageDir.getAbsolutePath(),decryptCallback)) { if (!isCancelled) { return ERROR_DECRYPT_OR_SIGN_FAILED; } } // 确保进度设置为100% currentVerifiedBytes = UpdateSize; // 阶段5: 解压升级包 currentPhase.set(PHASE_EXTRACTING); // 修复:重置解压计数器 currentExtractedBytes = 0; // 重置计数器 notifyProgress("解压升级包: " + packageName); // 解压时更新进度(修改) boolean extractResult = FileUtils.extractZipWithProgress( destFile, storageDir, (extracted, total) -> { currentExtractedBytes = extracted; notifyProgress(String.format("解压 %s: %s/%s", packageName, formatSize(extracted), formatSize(total))); } ); if (!extractResult) { lastErrorMessage = "解压失败: " + packageName; return ERROR_EXTRACT_FAILED; } // 删除已解压的升级包以节省空间 if (!destFile.delete()) { Log.w(TAG, "删除升级包失败: " + destFile.getName()); } // 更新解压进度(完成当前包) currentExtractedBytes += packageSize; } if (!mapDataController.checkInstallationStatus()) { notifyProgress("校验失败"); return ERROR_FILE_VERIFY_FAILED; } else { notifyProgress("校验成功"); } // MapDownloader.fromEngineAsync(sdkNativeEngine, new MapDownloaderConstructionCallback() { // @Override // public void onMapDownloaderConstructedCompleted(@NonNull MapDownloader downloader) { // Log.i(TAG, "数据同步成功"); // } // }); // 阶段5: 清理工作 currentPhase.set(PHASE_CLEANUP); notifyProgress("清理缓存..."); if (backupDir.exists() && !FileUtils.deleteRecursive(backupDir)) { Log.w(TAG, "删除备份文件失败"); } // 最终进度设为100% notifyProgress("更新完成"); return SUCCESS; } catch (InterruptedException e) { // 场景1:备份未完成时被取消 if (!isBackupCompleted &&backupDir != null && backupDir.exists()) { // backupFile.delete(); if (!FileUtils.deleteRecursive(backupDir)) { Log.w(TAG, "删除备份文件失败: " + backupDir.getAbsolutePath()); } } lastErrorMessage = "更新任务被中断"; return ERROR_USER_CANCELED; } catch (Exception e) { lastErrorMessage = "未知错误: " + e.getMessage(); Log.e(TAG, "更新失败", e); return ERROR_UNEXPECTED; } } @Override protected void onPostExecute(Integer resultCode) { if (resultCode == SUCCESS) { notifyListener(SUCCESS, "更新成功,请重启车机"); currentPhase.set(PHASE_IDLE); currentTask = null; } else { // 只有备份完成时才进行回滚(场景2) if (isBackupCompleted && backupDir != null && backupDir.exists()) { // 场景2:进入回滚流程 isRollingBack = true; currentPhase.set(PHASE_ROLLBACK); // 先发送回滚进度通知(99%) notifyProgress("更新失败,正在回滚数据..."); // 保存错误消息,因为回滚完成后还需要使用 final String errorMessage = lastErrorMessage; // 启动回滚线程 new Thread(new Runnable() { @Override public void run() { try { // 执行回滚 performRollback(backupDir); } finally { // 回滚完成后删除备份 // backupFile.delete(); if (backupDir.exists() && !FileUtils.deleteRecursive(backupDir)) { Log.w(TAG, "删除备份文件失败: " + backupDir.getAbsolutePath()); } if (!isCancelled) { // 回滚完成后发送最终结果 notifyListener(resultCode, getErrorMessage(resultCode)); } // 重置状态 currentPhase.set(PHASE_IDLE); currentTask = null; isRollingBack = false; } } }).start(); } else { // 场景1:没有备份文件,直接报告错误 notifyListener(resultCode, lastErrorMessage); currentPhase.set(PHASE_IDLE); currentTask = null; } } } } // ================== 启动时恢复检查 ================== private void checkRecoveryOnStartup() { // File backupFile = findLatestBackupFile(); Log.i(TAG, "=== 启动时初始化检查 ===" ); storageSize = FileUtils.getDirectorySize(storageDir); backupSize = FileUtils.getDirectorySize(backupDir); Log.i(TAG, "检测到备份数据大小为: " + backupSize); Log.i(TAG, "检测到storage数据大小为: " + storageSize); // 存在备份文件说明上次更新中断 if (backupDir != null && backupDir.exists()) { // long fileSize = backupFile.length(); // long fileSize = backupSize; //// long expectedSize = estimateBackupSize(); // long expectedSize = storageSize; // 场景3:备份未完成(文件大小小于预期大小的90%) if (backupSize < storageSize ) { isRollingBack = false; Log.i(TAG, "检测到未完成的备份,删除: " + backupDir.getName()); // backupFile.delete(); if (backupDir.exists() && !FileUtils.deleteRecursive(backupDir)) { Log.w(TAG, "删除备份文件失败: " + backupDir.getAbsolutePath()); } return; }else { // 场景4:备份已完成,启动回滚 Log.i(TAG, "检测到备份数据大小为: " + backupSize); Log.i(TAG, "检测到storage数据大小为: " + storageSize); Log.i(TAG, "检测到完整的备份,开始回滚: " + backupDir.getName()); currentPhase.set(PHASE_ROLLBACK); notifyProgress("检测到未完成更新,正在恢复数据..."); // 执行回滚 performRollback(backupDir); isRollingBack = true; // 删除备份文件 // backupFile.delete(); notifyProgress("数据恢复完成"); } } Log.i(TAG, "=== 初始化检查结束未发现备份 ===" ); isRollingBack = false; // this.updateListener = null;zwx } private void checkStoragePerformance() { long writeSpeed = StorageUtils.measureWriteSpeed(storageDir); Log.d(TAG, "存储写入速度: " + formatSize(writeSpeed) + "/s"); if (writeSpeed < 50 * 1024 * 1024) { // 低于 50MB/s Log.w(TAG, "检测到低速存储设备,还原操作可能较慢"); } } // 新增回滚方法-删除storage-复制备份目录到storage-删除备份目录 private void performRollback(File backupDir) { if (backupDir == null || !backupDir.exists() || !backupDir.isDirectory()) { Log.w(TAG,"error :备份目录无效或不存在 " ); } currentPhase.set(PHASE_COPYING); try { // 1. 设置回滚进度为99% backupSize = FileUtils.getDirectorySize(backupDir); Log.w(TAG, "开始删除更新目录: " + storageDir.getAbsolutePath()); notifyProgress("开始删除更新目录"); // 2. 删除更新后的数据并且保留storageDir文件夹 FileUtils.deleteDirectoryContents(storageDir); notifyProgress("开始恢复备份目录"); Log.w(TAG, "开始恢复备份目录: " ); try { // 执行目录复制 if (!singlecopydirectory.copyDirectoryWithProgress(backupDir,storageDir, (copied, total) -> { rollBackCopiedBytes = copied; notifyProgress("回滚中: " + formatSize(copied) + "/" + formatSize(total)); })) { throw new IOException("回滚失败: " + storageDir.getAbsolutePath() + " -> " + backupDir.getAbsolutePath()); } notifyProgress("恢复数据完成开始删除备份目录"); //deleteRecursive:删除整个目录 if (backupDir.exists() && !FileUtils.deleteRecursive(backupDir)) { Log.w(TAG, "删除备份文件失败: " + backupDir.getAbsolutePath()); notifyProgress("恢复数据删除失败"); isBackupCompleted = false; } notifyProgress("回滚完成"); isBackupCompleted = false; } catch (Exception e) { Log.e(TAG, "回滚过程中发生错误", e); // 可选:尝试二次备份或记录日志,这里直接抛出错误 notifyProgress("回滚过程中发生错误"); } } catch (Exception e) { Log.e(TAG, "回滚过程中发生错误", e); } } // 释放资源 public void release() { try { context.unregisterReceiver(usbReceiver); context.unregisterReceiver(vehicleReceiver); } catch (Exception e) { Log.w(TAG, "释放资源时出错", e); } } // ================== 接口定义 ================== public interface UpdateListener { void onProgress(int phase, float progress, String message); void onResult(int resultCode, String message); } public interface ProgressCallback { void onProgress(long progress, long total) throws InterruptedException; } public File getUsbRoot() { return usbRoot; } public File getCacheDir() { return cacheDir; } public File getStorageDir() { return storageDir; } public String getErrorMessage(int code) { return switch (code) { case ERROR_NO_USB -> "未检测到USB设备,请检查连接状态或更换接口"; case ERROR_NO_UPDATE_PACKAGE -> "升级包文件缺失,请确认存储路径"; case ERROR_BATTERY_LOW -> "电池电量不足(需≥20%)"; case ERROR_STORAGE_INSUFFICIENT -> "存储空间不足(需预留20gb以上)"; case ERROR_UPDATE_IN_PROGRESS -> "系统正在执行其他升级任务"; case ERROR_COPY_FAILED -> "文件复制失败,请检查存储权限"; case ERROR_EXTRACT_FAILED -> "升级包解压失败(可能文件损坏)"; case ERROR_USER_CANCELED -> "用户已取消升级操作"; case ERROR_UNEXPECTED -> "发生未预期的系统异常"; case ERROR_USB_REMOVED -> "升级过程中USB设备被移除"; case ERROR_VEHICLE_SHIFTED -> "请将车辆档位切换至P档"; case ERROR_BATTERY_TOO_LOW -> "电池电量极低(需≥10%)"; case ERROR_FILE_VERIFY_FAILED -> "文件校验失败(MD5/SHA256不匹配)"; case ERROR_DECRYPT_OR_SIGN_FAILED -> "文件解密/验签失败"; default -> "未知错误导致更新失败"; }; } void removeLegacy() { if (storageDir == null || !storageDir.exists() || !storageDir.isDirectory()) { return; } Pattern pattern = Pattern.compile(FILE_NAME_PATTERN); File[] files = storageDir.listFiles(); if (files == null) return; for (File file : files) { if (file.isFile() && pattern.matcher(file.getName()).matches()) { // 删除匹配的文件 try { Files.deleteIfExists(file.toPath()); } catch (IOException | SecurityException e) { // 处理异常(记录日志等) } } } // 删除sign文件夹(如果存在) Path signDir = Paths.get(storageDir.getAbsolutePath(), "sign"); if (Files.exists(signDir)) { try { // 递归删除整个目录 deleteDirectoryRecursively(signDir); } catch (IOException | SecurityException e) { // 处理异常 } } } private void deleteDirectoryRecursively(Path path) throws IOException { if (Files.isDirectory(path)) { // 使用 try-with-resources 确保 Stream 关闭 try (Stream<Path> children = Files.list(path)) { children.forEach(child -> { try { deleteDirectoryRecursively(child); } catch (IOException e) { // 处理子项删除异常 throw new UncheckedIOException(e); // 转换为 RuntimeException 以便在 Stream 中抛出 } }); } catch (UncheckedIOException e) { // 重新抛出原始 IOException throw e.getCause(); } } // 删除空目录或文件 Files.deleteIfExists(path); } }
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值