Hike News
Hike News

[瞎捣鼓] 把C盘转移的只剩Windows

想要把C盘保留的只剩Windows文件夹,那么就要把别的文件夹移出去,那怎么移出去呢?

危险命令警告 : 本文章中部分操作可能会造成严重后果,请备份好您的数据。

一、分析步骤

主要是以下步骤:

  1. 复制C盘下文件夹至其他盘
  2. 删除C盘的文件夹
  3. 创建C盘 文件夹<=> 的其他盘文件夹 的硬链接

而删除C盘文件的时候会存在已经运行的系统程序,所以要在PE系统或者Windows的系统恢复模式下的CMD执行操作。
而为了方便实现这些操作,就要使用以下这四个命令实现每个步骤。

二、解释命令

  1. shutdown 关机重启至Windows恢复模式
    我们使用时的命令为:
    1
    shutdown /r /o /t 0
    参数解析:
    /r : 重启计算机。
    /o : 转到高级启动选项菜单(恢复模式)并重新启动计算机。
    /t 0 : 将关闭前的超时时间设置为0秒。
  2. robocopy 复制C盘文件夹至其他盘
    我们使用时的命令为:
    1
    robocopy "sourcePath" "targetPath" /E /COPYALL /XJ /XD WindowsApps
    参数解析:
    "sourcePath" : 源目录
    "targetPath" : 目录路径
    /E : 复制所有子目录(包括空目录)。
    COPYALL : 复制所有文件信息
    /XJ : 排除符号链接的项目(避免因符号链接导致递归复制或错误)
    /XD WindowsApps : 排除名为 WindowsApps 的目录(常用于跳过系统或应用商店的敏感目录)。
  3. rmdir 删除C盘文件夹
    我们使用时的命令为:
    1
    rmdir "targetPath" /S /Q
    参数解析:
    rmdir : 删除目录的命令。
    "targetPath" : 目标目录(Windows 默认程序安装路径)。
    /S : 递归删除目录及其所有子目录和文件。
    /Q : 静默模式(不提示确认)。
  4. mklink 创建硬链接
    我们使用时的命令为:
    1
    mklink /J  "targetPath" "sourcePath"
    参数解析:
    /J : 创建目录联接(Junction)(适用于本地目录)
    "targetPath" : 新创建的链接路径(目标路径)
    "sourcePath" : 原始目录路径(源路径)

熟悉命令后,我们只要在Windows恢复模式的CMD或者PE系统运行就好了。但是鉴于Windows恢复模式下的CMD使用不便,而且我们的操作都是对C盘重要文件夹操作,所以还是提前编写脚本,之后再到运行就会方便而且安全很多。那具体脚本如何写呢?

三、具体脚本

由于重启命令只有一行而且没有风险,所以不必编写脚本,
那我们只要编写操作较多的复制、删除与链接脚本就好。
这里,我们把软件要转移的盘设置为D盘,数据要转移的盘设置为E盘,若要转换别的盘针对性修改参数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SET ProgPath=D:
SET DataPath=E:
echo ---------Moveing "Program Files (x86)"---------
robocopy "C:\Program Files (x86)" "%ProgPath%\Program Files (x86)" /E /COPYALL /XJ /XD WindowsApps
rmdir "C:\Program Files (x86)" /S /Q
mklink /J "C:\Program Files (x86)" "%ProgPath%\Program Files (x86)"
echo -------------Moveing "Program Files"-----------
robocopy "C:\Program Files" "%ProgPath%\Program Files" /E /COPYALL /XJ /XD WindowsApps
rmdir "C:\Program Files" /S /Q
mklink /J "C:\Program Files" "%ProgPath%\Program Files"
echo ----------------Moveing "Users目录"----------------
robocopy "C:\\Users" "%DataPath%\Users" /E /COPYALL /XJ /XD WindowsApps
rmdir "C:\\Users" /S /Q
mklink /J "C:\\Users" "%DataPath%\Users"

将以上脚本修改保存以.bat为文件后缀名,比如mv.bat,存放到你能找到的地方。

四、运行脚本

接下来,运行以下命令便会进入到Windows的恢复模式:

先不要运行,先看完以下步骤再运行命令

1
shutdown /r /o /t 0

之后在打开的蓝色界面中,按照以下步骤以此点击即可进入Windows的恢复模式中的CMD:

1
故障排除 -> 高级选项 -> 命令提示符

之后,在命令行中输入保存的.bat后缀的脚本文件路径,回车运行等待运行完成即可。
若Windows的恢复模式中没有以上选项,可以去使用PE系统操作,网络上具体方法较多,自己查询即可。

[Java] Java打包jar文件

Java打包jar文件

1.编译.java文件

通过javac命令编译.java后缀的源码文件

1
2
3
4
javac -d 生成目录 源码目录路径或源码文件路径
# 例如:
javac -d ./Build project/test/app/
javac -d ./Build project/test/*.java

2.打包.class文件

打包时一般使用cfvm或者cfve参数,

  • cfvm参数使用.MF文件来指定打包清单,和决定主类(入口)
  • cfve参数是在输入打包命令的参数指定主类
  1. 使用cfvm参数打包,
    配置.MF文件,名字可以自定义,我这里使用CONFIG.MF这个文件名
    1
    2
    3
    4
    Manifest-Version: 1.0
    Created-By: 23.0.1 (Oracle Corporation)
    Main-Class: project.test.app.Main
    # 此处应为空行
    其中,Main-Class配置项,填写编译后的主类路径(注意不要加.class)
    注意:
    • 主类路径是对于打包时选定的路径开始的(包含选取的目录)
    • 冒号后面加一个空格
    • 最后行必须为空行
1
2
cd Build
jar -cfvm Out.jar CONFIG.MF ./project
  • Out.jar 为打包后的打包文件名
  • CONFIG.MF为刚刚配置的.MF文件
  • ./project 为刚刚javac编译完成的目录

命令运行结束后,目录下会出现Out.jar文件

  1. 使用cfve参数打包
    打包命令如下:
    1
    2
    cd Build
    jar -cfve Out.jar project.test.app.Main ./project
    • Out.jar 为打包后的打包文件名
    • project.test.app.Main为填写编译后的主类路径(注意不要加.class)
    • ./project 为刚刚javac编译完成的目录

命令运行结束后,目录下会出现Out.jar文件

3.运行与导入jar

  1. 运行jar
    在刚刚打包完成的.jar文件目录打开cmd,输入如下命令即可运行:

    1
    java -jar Out.jar

    其中,Out.jar就是我们刚刚生成的.jar文件的文件名

  2. 导入jar
    jar文件还可以在其他java程序编译或运行时导入
    秩序将java程序需要的包进行打包,并在运行时添加如下参数即可:

    1
    2
    3
    4
    # 编译时导入示例
    javac -cp "path/to/classes;path/to/lib.jar" MyClass.java
    # 运行时导入示例
    java -cp "path/to/classes;path/to/lib.jar" my.package.MainClass
  • path/to/classes;是我们需要导入的.class文件的路径(可忽略)
  • path/to/lib.jar是我们需要导入的.jar文件的路径
  • MyClass.java是我们编译时的java源码文件
  • my.package.MainClass是我们运行时的java源码文件路径

[Python] embed版Python打包使用

embed版Python打包使用

参考引用
embed版Python如何安装第三方包
【Python打包】Python embed打包方法 亲测有效
如何将 Tk 套件加入到嵌入式 Python 中

记录一下embed版Python打包使用
embed版Python就是精简纯净的Python,非常适合打包发布。

1.下载embed版Python

2.配置._pth文件与安装pip

  1. 在解压好的文件目录中,编辑._pth后缀的文件

    1
    2
    3
    4
    5
    python310.zip
    # 这里添加想要导入的模块路径,是相对于解释器的路径

    # Uncomment to run site.main() automatically
    #import site # 取消注释以启用第三方库。
  2. 将其中 #import site前的#删除,以取消注释,使第三方库生效

  3. 下载 get-pip.py(点击下载)文件,复制到之前python的解压的目录中(python解释器程序同级目录)。

  4. 解压的目录下打开cmd,输入以下命令:

    1
    .\python get-pip.py

3.安装第三方库

在python的目录下打开cmd,输入以下命令安装第三方库

1
.\python -m pip install 第三方库名

可增加以下参数使用清华镜像源下载:

1
.\python -m pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 第三方库名

4.打包发布

之后直接将源代码及其python目录压缩发布即可

[数据结构] 线段树笔记

线段树

简述何为线段树

参考文章
【学习笔记】详解线段树(浅显易懂,匠心之作,图文并茂)

简单来说,是通过原数组生成一个的一个二叉树,其中,树的每一个非叶子节点都是由其子节点通过一些算数运算生成的,比如求解区间和,就是每个节点对子节点求和生成的,每个节点存储的就是子节点的和,

(node.value = node.left.value + node.right.value)

或者区间最大值的树,就是取其中最大值生成的,每个节点就是子节点中的最大值

(node.value = max(node.left.value, node.right.value))

线段树从叶子依次向上生成的。那么来说,每个非叶子节点的值
就是代表所有子节点的的有效值(或者说是特征(最大最小))的节点,每个节点代表的区间就是对应着所有子节点区间的合并区间。
例如叶子节点为原数组的值,那么其父节点的值就是其与兄弟节点的和(或者取最大最小值),
其父节点的所代表的区间就是其节点的区间与兄弟节点的区间的合并区间。
(因为每个叶子节点代表的区间就是他自己本身,所以其节点的区间与兄弟节点的区间的合并区间就是它们本身)

其逻辑结构形如下图 (区间和的线段树):

区间和的线段树

但是线段树以顺序表存储会处理更加方便,所以采用顺序结构存储,其存储结构形如下图,

root node 1 node 2 ... 原数组元素 0 原数组元素 1 ... 原数组元素 n
如果原数组元素的大小为n,那么原数组的元素从n处复制存储

其中每一个节点都有四个属性:

  • start : 节点所代表区间的开始地址
  • end : 节点所代表区间的结束地址
  • value : 节点所代表区间的总有效值
  • lazy : 节点所代表区间的修改值

其中根节点为坐标为0,每一个地址为i的节点的左右孩子地址分别为i*2i*2+1,父节点为i/2
其中线段树的主要作用是数组区间频繁查询或修改

区间频繁查询:

查询涉及分治思想,分解,解决,合并。
若要查找的区间[l,r]与当前节点所包含的区间[start,end]完全相同
(即node.start=l 且node.end=r)
那么直接返回当前节点的值,因为当前节点就代表其包含区间的总有效值
若是从树顶至下查找区间[l,r]并不是与当前节点完全相同,那么无非三种情况,

  1. 要查找区间完全在当前节点完全在当前节点的左节点的区间
    • 那么直接递归进入左子树继续查询
  2. 要查找区间完全在当前节点完全在当前节点的右节点的区间
    • 那么直接递归进入右子树继续查询
  3. 要查找区间完全在一部分在左节点的区间,一部在右节点的区间
    • 将要查找的区间[l,r] 分为两部分[l, m][m,r] 其中m = (l+r)/2
    • 再对左子树查询[l,m],右子树查找[m,r]

以上情况会一直递归至要查找的区间[l,r]与当前节点所包含的区间[start,end]完全相同
那么就会递归返回其节点的值,其值即为最终的值。

区间频繁修改

区间频繁修改需要给每个节点添加一个新的属性lazy,
每个节点的lazy属性标志着其区间的修改标志,
例如,在一棵对于求区间和的线段树中,如果修改代表区间[l,r]区间的节点node的lazy为10
就代表在[l,r]这个区间中,每个值都添加10,但是不需要修改其所有子节点的值,只需要知道
当前节点的所有子节点个数n,再返回node.value + 10 * n即可。
设置区间的lazy的过程与查询区间相同,从线段树的根节点开始依次向下查找,
查找到符合区间的节点之后,设置其lazy属性即可。

单点查询与修改

单点查询

如果想要在树中查询某个元素(叶子节点)的值,那么从根节点开始向下查找
在递归的过程中,若在当前当前节点的区间已经被修改,即lazy不为0,
那么在递归查找子节点时,就需要把当前节点的lazy设置给子节点,并将当前节点的lazy置零
这个操作叫做下放pushdown
直至查找到要查询的节点元素时,通过lazy值来对当前节点的值修改
并把lazy置零,返回当前节点修改后的值

单点修改

单点修改在单点查询的基础上,找到要修改的节点元素,再对节点进行修改
之后,从当前节点向父节点递归修改父节点的值,直至根节点
因为对单个叶子节点进行修改,可能会改变包含本节点区间的节点的值
(也可能不会,比如在区间最大值的线段树中,修改后的节点值仍然比兄弟节点小),
所以需要对父节点进行递归修改。

[数据结构] 差分数组+前缀和

差分数组+前缀和

参考文章

差分数组的推导公式:
原数组为num,差分数组为diff,前缀和数组为sum,

1
2
diff[0] = num[0]
diff[i] = num[i] - num[i-1]

前缀和的推导公式:

1
2
sum[0] = num[0]
sum[i] = num[i] + sum[i-1]
  • 差分数组主要用来大范围的区间修改
  • 前缀和主要用来大范围的区间查询

举个例子,假设原数组为num,差分数组为diff,前缀和数组为sum

  • 建立差分数组
0 1 2 3 4
num 1 3 5 2 8
diff(num) 1 3-1 5-3 2-5 8-2
diff 1 2 2 -3 6
  • 建立前缀和数组
0 1 2 3 5
num 1 4 8 6 14
sum(num) 1 +4 +8 +6 +14
sum 1 5 13 19 33

差分数组:

当需要对原数组元素区间频繁修改值时,
只需要对差分数组的做简单修改就可以,
例如要修改原数组范围[1,3]的元素全部+2,
只需要修改差分数组的diff[1] += 2和diff[3+1] -= 2
即:

  • 当要修改区间[l,r]的元素全部+k时,只需要修改diff[l] += kdiff[r+1] -= k即可。
1
2
diff[l] += k
diff[r+1] -= k

前缀和数组:

当需要对原数组元素区间元素的和频繁查询时,
只需要建立前缀和数组就可大大提升查询速度,
例如要查询原数组范围[1,3]的元素和,
原数组 num = {1, 3, 5, 2, 8 }
前缀和数组 sum = {1, 4, 8, 6, 14 }
那么查询[1,3]的元素和,只需要查询sum[3] - sum[1-1]即可。
即:

  • 当要查询区间[l,r]的元素和时,只需要查询sum[r] - sum[l-1]即可。

取回原数组

  • 若要取回原数组,只需要对差分数组进行建立前缀和数组即是原数组。
  • 同样的,只需要对前缀和数组进行建立差分数组也是原数组。
1
2
num = sum(diff)
num = diff(sum)

例子: 对差分数组进行建立前缀和数组即是原数组

0 1 2 3 4
num 1 3 5 2 8
diff(num) 1 3-1 5-3 2-5 8-2
diff 1 2 2 -3 6
sum(diff) 1 +2 +2 -3 +6
num 1 3 5 2 8

例子: 对前缀和数组进行建立差分数组是原数组

0 1 2 3 4
num 1 4 8 6 14
sum(num) 1 +4 +8 +6 +14
sum 1 5 13 19 33
diff(sum) 1 5 - 1 13- 5 19-13 33-19
num 1 4 8 6 14

[C++] C++生成DLL与LIB并调用

参考文章:

DLL 就是把一个C/CPP程序功能作为文件供其它程序调用

  • dll文件用于动态调用,在程序运行时调用,dll文件修改动能调用方程序也不需要重新编译
  • lib文件用于静态调用,在编译时与调用方一起编译链接,编译出来的程序会比动态调用大

1.创建想要作为DLL的C源文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// test.c

#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) int __stdcall add(int, int);
__declspec(dllexport) int __stdcall sum(int *nums, int size);
#ifdef __cplusplus
}
#endif

int __stdcall add(int a, int b)
return a + b;


int __stdcall sum(int *nums, int size)
{
int sum = 0;
for (int i = 0; i < size; i++)
sum += nums[i];
return sum;
}

这里定义了两个简单的函数,add和sum
之后通过编译器生成dll文件或lib文件,供主程序调用

2.动态调用方式:

  1. 生成dll

    1
    2
    3
    4
    5
    # GCC 编译器
    gcc -shared .\test.c -o test.dll
    # MSVC 编译器
    cl -c test.c
    link -DLL -out:test.dll test.obj
  2. 调用dll

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // main.c
    #include <stdio.h>
    #include <windows.h>

    int main()
    {
    // 加载dll文件
    HMODULE hdll = LoadLibrary("test.dll");
    if (!hdll)
    {
    printf("未能加载DLL");
    return 0;
    }
    // 定义函数指针用于接收需要调用的函数
    typedef int (*FUNC_ADD_T)(int, int);
    typedef int (*FUNC_SUM_T)(int *, int);
    // 获取函数指针
    FUNC_ADD_T add = (FUNC_ADD_T)GetProcAddress(hdll, "add");
    FUNC_SUM_T sum = (FUNC_SUM_T)GetProcAddress(hdll, "sum");
    if (!add || !sum)
    {
    printf("未能加载函数");
    return 0;
    }
    int nums[5] = {1, 2, 3, 4, 5};
    printf("add函数测试 1 + 2 = %d\n", add(1, 2));
    printf("待累加数组为: ");
    for (int i = 0; i < 5; i++)
    printf("%d ", nums[i]);
    printf("\nsum函数测试输出结果: %d", sum(nums, 5));
    }
  3. 编译运行输出结果:

    出现乱码可以添加 --exec-charset=gbk编译选项

    1
    2
    3
    4
    $ gcc .\main.c -o .\main.exe && .\main.exe
    add函数测试 1 + 2 = 3
    待累加数组为: 1 2 3 4 5
    sum函数测试输出结果: 15

3.静态生成方式

  1. 生成lib

    1
    2
    3
    4
    # GCC生成lib
    gcc -c .\test.c -o test.lib
    # MSVC生成lib,obj,exp文件
    cl /Dld .\test.c
  2. 主函数调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // main.c
    #include <stdio.h>
    #include <windows.h>
    // 声明外部函数
    extern int add(int a, int b);
    extern int sum(int *nums, int size);
    int main()
    {
    int nums[5] = {1, 2, 3, 4, 5};
    printf("add函数测试 1 + 2 = %d\n", add(1, 2));
    printf("待累加数组为: ");
    for (int i = 0; i < 5; i++)
    printf("%d ", nums[i]);
    printf("\nsum函数测试输出结果: %d", sum(nums, 5));
    }
  3. 编译运行
    -L参数指定要调用的lib文件所在目录,
    -l参数指定要调用的lib文件名(不包含文件类型后缀)

    1
    2
    3
    4
    gcc .\main.c -L. -ltest -o main.exe && ./main.exe
    add函数测试 1 + 2 = 3
    待累加数组为: 1 2 3 4 5
    sum函数测试输出结果: 15

在Python中调用dll中的函数

  1. 调用dll中的函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # main.py
    import ctypes as C
    from os.path import abspath

    # 加载动态链接库
    dll = C.cdll.LoadLibrary(abspath("test.dll"))
    # 设置函数参数类型和返回值类型
    dll.sum.argtypes = [C.POINTER(C.c_int), C.c_int]
    dll.sum.restype = C.c_int
    # 生成测试数据
    arr = (C.c_int * 5)(1, 2, 3, 4, 5)
    # 调用函数
    result1 = dll.add(10, 20)
    result2 = dll.sum(arr, 5)
    print("add 输出结果: ", result1)
    print("待测试数组: ", arr[:])
    print("sum 输出结果: ", result1)
  2. 运行输出结果
    1
    2
    3
    4
    $ python main.py
    add 输出结果: 30
    待测试数组: [1, 2, 3, 4, 5]
    sum 输出结果: 30

[C++] Cpp socket通信

C++ Boost.Asio实现Socket局域网通信

需要的头文件:

1
2
3
4
5
#include <iostream>       // 输入输出
#include <fstream> // 文件操作
#include <string> // 字符串操作
#include <filesystem> // 文件操作
#include <boost/asio.hpp> // socket通信

笔记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tcp::resolver resolver = tcp::resolver(context);           // 解析器
tcp::endpoint ep = *resolver.resolve("127.0.0.1", "5555"); // 解析域名
tcp::resolver resolver(io); // 解析器
tcp::resolver::query q("www.baidu.com", "https"); // 查询
tcp::resolver::iterator eq2 = resolver.resolve(q); // 迭代器
tcp::endpoint ep = *resolver.resolve("127.0.0.1", "5555"); // 解析域名
tcp::resolver resolver(io); // 解析器
tcp::resolver::query q("www.baidu.com", "https"); // 查询
tcp::resolver::iterator eq2 = resolver.resolve(q); // 迭代器
sock.send(...); // 发送数据
sock.write_some(boost::asio::buffer("hello world", sizeof("hello world")));
sock.async_send(...); // 异步
io_context.run();
是一个成员函数,它的作用是启动异步操作的处理循环
sock.shutdown(tcp::socket::shutdown_both); // 关闭连接

客户端源代码:

预处理和命名空间:

1
2
3
4
5
6
// send.cpp
#define PORT 5555
#define BUF_SIZE 1024
#define Error -1
using namespace std;
using boost::asio::ip::tcp;

发送文件函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int send_file(tcp::socket &sock, std::string filename)
{
std::ifstream file(filename, std::ios::binary);
if (!file)
return Error;
char buffer[BUF_SIZE]; // 分配内存
while (file)
{
file.read(buffer, BUF_SIZE); // 读取文件
if (file.gcount() < BUF_SIZE)
{
sock.send(boost::asio::buffer(buffer, file.gcount()));
return 0;
}
sock.send(boost::asio::buffer(buffer, BUF_SIZE));
}
return 0;
}

参数解析函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int Parameter_parsing(int argc, char **argv, string &FilenameOrMsg, int &Is_File, boost::asio::ip::address &ip)
{
if (argc != 3)
return Error;
else
{
boost::asio::ip::address _ip;
boost::system::error_code ec;
_ip = boost::asio::ip::address::from_string(argv[1], ec);
if (ec)
return Error;
ip = _ip;
FilenameOrMsg = string(argv[2]);
if (filesystem::exists(FilenameOrMsg) && filesystem::is_regular_file(FilenameOrMsg))
Is_File = 1;
return 1;
}
}

主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
int main(int argc, char **argv)
{
try
{

boost::asio::io_context io_context;
tcp::socket sock(io_context);
boost::asio::ip::address TargetIp;
string FilenameOrMsg;
string &FileName = FilenameOrMsg;
string &Msg = FilenameOrMsg;
int Is_File = 0;
if (Parameter_parsing(argc, argv, FilenameOrMsg, Is_File, TargetIp) == Error)
{
// 输出提示
cout << "Usage: ./send <ip> <filename or message>" << endl;
return -1;
}
sock.connect(tcp::endpoint(TargetIp, PORT));
if (Is_File)
{
string _FileSize = std::to_string(std::filesystem::file_size(FileName));
string _FileName = filesystem::path(FilenameOrMsg).filename().string();
string _info = _FileName + '|' + _FileSize;
size_t _info_Size = _info.size();
char _info_Size_buffer[sizeof(size_t)];
memcpy(_info_Size_buffer, &_info_Size, sizeof(size_t));
char Mode[10] = "FILE MODE";
sock.send(boost::asio::buffer(Mode, 10));
sock.send(boost::asio::buffer(_info_Size_buffer, sizeof(size_t)));
sock.send(boost::asio::buffer(_info, _info_Size));
}
else
{
char Mode[10] = "MSGS MODE";
sock.send(boost::asio::buffer(Mode, 10));
size_t _Msg_Size = Msg.size();
char _Msg_Size_buffer[sizeof(size_t)];
memcpy(_Msg_Size_buffer, &_Msg_Size, sizeof(size_t));
int len = sock.send(boost::asio::buffer(_Msg_Size_buffer, sizeof(size_t)));
}
char confirm_send = 0;
sock.read_some(boost::asio::buffer(&confirm_send, sizeof(char)));
if (Is_File)
if (confirm_send || sock.is_open())
send_file(sock, FileName);
else
cout << "对方已拒绝接受文件!" << endl;
else
sock.send(boost::asio::buffer(Msg, Msg.size()));
sock.close();
}
catch (const boost::system::system_error &e)
{
cout << "无法链接至目标主机!" << '\n';
}
return 0;
}

服务端源代码:

确认文件是否接收函数:

1
2
3
4
5
6
7
8
9
10
11
12
bool Confirm(string FileName, size_t FileSize)
{
cout << "文件名: " << FileName << endl;
cout << "文件大小: " << FileSize << endl;
cout << "是否接收文件(Y/N): ";
char ch;
cin >> ch;
if (ch == 'Y' || ch == 'y')
return 1;
else
return 0;
}

接收文件函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
int recv_file(tcp::socket &sock, std::string &file_name)
{
std::ofstream ofs(file_name, std::ios::binary);
char buf[BUF_SIZE];
while (true)
{
int len = sock.read_some(boost::asio::buffer(buf, BUF_SIZE));
ofs.write(buf, len);
if (len < BUF_SIZE)
break;
}
return OK;
};

主函数部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
int main(void)
{
cout << "开始接收数据!" << endl;
boost::asio::io_context context;
tcp::socket sock(context);
tcp::endpoint ep(boost::asio::ip::address::from_string("0.0.0.0"), PORT);
tcp::acceptor acceptor(context, ep);
acceptor.accept(sock);
char Mode[10] = "FILE MODE";
string FileName;
size_t FileSize;
size_t InfoSize;
sock.read_some(boost::asio::buffer(Mode, 10));
// 将整数转换为字节流并存储到字符数组中
char _InfoSize_buf[sizeof(size_t)];
sock.read_some(boost::asio::buffer(_InfoSize_buf, sizeof(size_t)));
std::memcpy(&InfoSize, _InfoSize_buf, sizeof(size_t));
if (!strcmp("FILE MODE", Mode))
{
char _File_NameSzie[InfoSize + 1];
int Index = 0;
int len = sock.read_some(boost::asio::buffer(_File_NameSzie, InfoSize));
_File_NameSzie[len] = '\0';
for (auto &c : _File_NameSzie)
{
Index++;
if (c == '|')
{
string _StrT_FileSize = string(_File_NameSzie).substr(Index, InfoSize);
FileSize = stoi(_StrT_FileSize);
break;
}
FileName = FileName + c;
}
if (Confirm(FileName, FileSize))
{
char Is_Confirm = 1;
sock.send(boost::asio::buffer(&Is_Confirm, sizeof(char)));
recv_file(sock, FileName);
cout << "文件接收成功!" << endl;
}
else
return 0;
}
else
{
cout << "来自 " << sock.remote_endpoint().address() << " 的消息: " << endl;
char confirm_send = 0;
sock.send(boost::asio::buffer(&confirm_send, sizeof(char)));
char _Msg_buf[InfoSize + 1];
int len = sock.read_some(boost::asio::buffer(_Msg_buf, InfoSize));
_Msg_buf[len] = '\0';
cout << _Msg_buf << endl;
}
sock.close();
return 0;
}

[C++] Cpp批量重命名文件

Cpp 批量重命名

主要使用的类与方法:

  • std::stoi : 字符串转数字
  • std::ofstream : 文件输出流
  • std::filesystem : 文件系统库
  • std::filesystem::path : 路径类
  • std::filesystem::rename : 重命名文件
  • std::filesystem::extension : 获取文件后缀
  • std::filesystem::absolute : 获取绝对路径
  • std::filesystem::current_path : 获取当前路径
  • std::filesystem::path::generic_string : 获取路径字符串
  • std::filesystem::temporary_directory_path : 获取临时目录
  • std::filesystem::directory_iterator: 遍历文件夹
  • std::filesystem::exists : 判断文件是否存在
  • std::filesystem::is_directory : 判断是否为文件夹
  • std::filesystem::is_symlink : 判断是否为软链接
  • std::filesystem::append_directory_options : 追加选项

需要用的头文件:

1
2
3
4
#include <iostream>   // 输入输出流
#include <filesystem> // 文件系统库
#include <string> // 字符串类
#include <fstream> // 文件输出流

全部的源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
namespace fs = std::filesystem;

int main(int argc, char const *argv[])
{
// 参数
std::string parameters[argc];
// 日志文件
std::string LogFilePath = fs::temp_directory_path().append("log.txt").generic_string();
std::ofstream Logs(LogFilePath);
int OpenLog = 0;
// 重命名参数
std::string BaseName = "";
std::string TargetExt = "";
// 输出参数
fs::path OutDir = fs::current_path();
// 统计
int FileCount = -1;

// 输出帮助信息
auto OutHelpInfo = []()
{
std::cout << "批量重命名文件" << std::endl;
std::cout << "参数说明:" << std::endl;
std::cout << " -h, --Help 显示帮助信息" << std::endl;
std::cout << " -b, --Base 设置重命名的基准文件名" << std::endl;
std::cout << " -l, --Log 开启日志保存重命名记录" << std::endl;
std::cout << " -c, --FileCount 设置重命名文件的个数" << std::endl;
std::cout << " -o, --OutDir 设置重命名文件的输出目录" << std::endl;
std::cout << " -e, --Ext 指定需要重命名文件的后缀名(含分隔符\".\")" << std::endl;
};

auto UpperStr = [](std::string str)
{
std::string result;
for (auto &c : str)
result += toupper(c);
return result;
};
if (argc < 3)
{
OutHelpInfo();
return -1;
}
// 参数解析
for (int i = 0; i < argc; i++)
{
parameters[i] = argv[i];
if (parameters[i] == "-h" || parameters[i] == "--help")
{
OutHelpInfo();
return 0;
}
else if (parameters[i] == "-b" || parameters[i] == "--Base")
{
if (i + 1 < argc)
BaseName = argv[i + 1];
else
{
std::cout << "错误: 参数 -b 缺少参数" << std::endl;
OutHelpInfo();
return -1;
}
}
else if (parameters[i] == "-l" || parameters[i] == "--Log")
OpenLog = 1;
else if (parameters[i] == "-c" || parameters[i] == "--FileCount")
{
if (i + 1 < argc)
FileCount = std::stoi(argv[i + 1]);
else
{
std::cout << "错误: 参数 -c 缺少参数" << std::endl;
OutHelpInfo();
return -1;
}
}
else if (parameters[i] == "-o" || parameters[i] == "--OutDir")
{
if (i + 1 < argc)
OutDir = fs::absolute(argv[i + 1]);
else
{
std::cout << "错误: 参数 -o 缺少参数" << std::endl;
OutHelpInfo();
return -1;
}
}
else if (parameters[i] == "-e" || parameters[i] == "--Ext")
{
if (i + 1 < argc)
TargetExt = UpperStr(std::string(argv[i + 1]));
else
{
std::cout << "错误: 参数 -e 缺少参数" << std::endl;
OutHelpInfo();
return -1;
}
}
}

if (!OpenLog)
Logs.close();

// 输出参数
std::cout << "基准文件名: " << BaseName << std::endl;
if (OpenLog)
std::cout << "已开启开启日志" << std::endl;
if (FileCount != -1)
std::cout << "重命名文件个数: " << FileCount << std::endl;
if (TargetExt != "")
std::cout << "目标文件后缀: " << TargetExt << std::endl;
std::cout << "输出目录: " << OutDir.generic_string() << std::endl;
std::cout << "是否开始批量重命名文件?(Y/N): ";

// 确认
char c;
std::cin >> c;
if (c != 'Y' && c != 'y')
return 0;

fs::path currentPath = fs::current_path();
int count = 1, ID = 0;
// 遍历
for (auto &file : fs::directory_iterator(currentPath))
{
// 达到指定个数退出
if (FileCount != -1 && count > FileCount)
break;
// 跳过文件夹
if (file.is_directory())
continue;
// 确认指定后缀
std::string Extension = UpperStr(file.path().extension().generic_string());
if (!TargetExt.empty() && Extension != TargetExt)
continue;
// 获取目标文件名
fs::path ToName = OutDir / (BaseName + std::to_string(ID++) + Extension);
// 跳过已重命名文件
if (fs::exists(ToName))
{
if (OpenLog)
{
std::cout << "文件 \"" << ToName << "\" 已存在, 跳过。" << std::endl;
ID = ID + 1;
}
continue;
}
// 跳过软链接
if (fs::is_symlink(file))
{
if (OpenLog)
std::cout << "文件 \"" << file.path() << "\" 是软链接, 跳过。" << std::endl;
continue;
}
// 输出日志
if (OpenLog)
{
Logs << file.path() << " => " << ToName << std::endl;
std::cout << "Renaming: " << file.path() << " => " << ToName << std::endl;
}
// 重命名
fs::rename(file.path(), ToName);
count++;
}
// 保存日志
if (OpenLog)
{
Logs.close();
fs::rename(LogFilePath, currentPath / "log.txt");
std::cout << "重命名完成,日志保存在: " << (currentPath / "log.txt").generic_string() << std::endl;
}
return 0;
}

感谢你看到到这里!