枚举指定路径下的制定类型文件

本文介绍了一个C++工具类,用于在指定目录下迭代并查找特定扩展名的文件。该类通过递归搜索目录,支持用户回调函数来处理每个找到的文件,并能筛选出符合指定扩展名的文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#pragma once

//枚举指定路径下的制定类型文件
//@LPCTSTR 文件全路径
//@pUserData 用户参数数据?
//@return 是否是用户取消
typedef BOOL (CALLBACK * LookUpFileCallBack)(LPCTSTR/*文件名称(包含全路径)*/);

#ifdef COMDLL_EXPORTS
#define COMDLL_API __declspec(dllexport)
#else
#define COMDLL_API __declspec(dllimport)
#endif

class COMDLL_API CEnumFile //导入类
{
public:

	CEnumFile(LPCTSTR lpszPath,LPCTSTR lpszFileExt, LookUpFileCallBack pCallBack);
	//回调获取文件的全路径信息
	//@lpszPath 目录名称
	//@lpszFileExt 指定的文件扩展名
	//@bRecursion 递归,循环;递归式
	//@bEnumFiles 是否迭代枚举所有文件
	//@pCallBack 回调函数,每一个文件调用一次
	//@pUserData ???用户参数
	//调用方式:CEnumFile::CEnumFile(CGlobalData::strCurrPath + _T("LNG\\"),_T(".lng"),EnumLngFile);
	CEnumFile(LPCTSTR lpszPath,LPCTSTR lpszFileExt,LookUpFileCallBack pCallBack,BOOL bRecursion/*=FALSE*/, BOOL bEnumFiles/*=TRUE*/);
public:
	~CEnumFile(void);
};
 
#include "stdafx.h"
#include "EnumFile.h"
#include <windows.h>

CEnumFile::CEnumFile(LPCTSTR lpszPath,LPCTSTR lpszFileExt,LookUpFileCallBack pCallBack)
{
	::CEnumFile(lpszPath,lpszFileExt,pCallBack,FALSE,FALSE);
}
CEnumFile::CEnumFile(LPCTSTR lpPath,LPCTSTR lpszFileExt,LookUpFileCallBack pCallBack,BOOL bRecursion, BOOL bEnumFiles)
{
	static BOOL s_bUserBreak = FALSE;
	try{
		//-------------------------------------------------------------------------
		if(s_bUserBreak) return;
		int len = lstrlen(lpPath);
		if(lpPath==NULL || len<=0) return;

		//NotifySys(NRS_DO_EVENTS, 0,0);
		CString path=_T("");
		path.Format(_T("%s"),lpPath);
		//if (str.Right(1)!='\\')//一种判断
		if(path.ReverseFind('\\')!=1)//二种判断
			path.Format(path+"%s","\\");
		path.Format(path+"%s","*");

		/*char path[MAX_PATH];
		strcpy(path, (const char*)lpPath);
		if(lpPath[len-1] != '\\') 
			strcat(path, "\\");
		strcat(path, "*");*/

		WIN32_FIND_DATA fd;
		HANDLE hFindFile = FindFirstFile((LPCTSTR)path, &fd);
		if(hFindFile == INVALID_HANDLE_VALUE)
		{
			::FindClose(hFindFile); 
			return;
		}

		CString tempPath; 
		BOOL bUserReture=TRUE; 
		BOOL bIsDirectory;

		BOOL bFinish = FALSE;
		while(!bFinish)
		{
			tempPath.Format(_T("%s"),lpPath);
			if(path.ReverseFind('\\')!=1)//二种判断
				path.Format(path+"%s","\\");
			path.Format(path+"%s",fd.cFileName);

			bIsDirectory = ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);

			//如果是.或..
			CString s1;
			s1.Format(_T("%s"),fd.cFileName);
			if( bIsDirectory && (s1.CompareNoCase(_T("."))==0 || s1.CompareNoCase(_T(".."))==0))
			{        
				bFinish = (FindNextFile(hFindFile, &fd) == FALSE);
				continue;
			}

			if(pCallBack && bEnumFiles!=bIsDirectory)
			{
				if(lpszFileExt!=NULL)//判断,查找指定扩展名的文件
				{
					int idx=s1.ReverseFind('.');
					if (-1!=idx)
					{
						CString tmp=s1.Right(lstrlen(s1)-idx);
						if(tmp.CompareNoCase(lpszFileExt)==0)
						{
							bUserReture = pCallBack(tempPath+s1);
							if(bUserReture==FALSE)
							{
								s_bUserBreak = TRUE; 
								::FindClose(hFindFile); 
								return;
							}
						}
					}
					/*char* pdot;
					char* ch = new char[tempPath.GetLength()+1];
					//
					char szStr[MAX_PATH] = {0};
					wcstombs(szStr, tempPath, tempPath.GetLength());
					const char * p = szStr;
					//UNICODE下宽字符的CString转换为const char *  http://www.cnblogs.com/sunnyjones/archive/2009/02/24/1397529.html
					strncpy(ch, p, tempPath.GetLength()+1);
					//tempPath.GetBuffer(tempPath.GetLength()+1);
					if((pdot = strrchr(ch, '.')) && stricmp(pdot, (const char *)lpszFileExt) == 0)
					{
						bUserReture = pCallBack(tempPath);
						if(bUserReture==FALSE)
						{
							s_bUserBreak = TRUE; 
							::FindClose(hFindFile); 
							return;
						}
					}*/
				}
				else//没有限定扩展名
				{
					bUserReture = pCallBack(tempPath);
					if(bUserReture==FALSE)
					{
						s_bUserBreak = TRUE; 
						::FindClose(hFindFile); 
						return;
					}
				}
			}

			//NotifySys(NRS_DO_EVENTS, 0,0);

			if(bIsDirectory && bRecursion) //是子目录
			{
				CEnumFile(tempPath, lpszFileExt, pCallBack, bRecursion, bEnumFiles);
			}

			bFinish = (FindNextFile(hFindFile, &fd) == FALSE);
		}

		::FindClose(hFindFile);

		//-------------------------------------------------------------------------
	}catch(...)
	{ 
		ASSERT(0); 
		return; 
	}
}
CEnumFile::~CEnumFile()
{
	
}

 我的项目中,vc++下需要迭代出指定目录下指定扩展名的文件的工具类

<think>我们正在开发一个Java批量文件重命名工具。根据用户需求,我们需要实现一个简单的工具,允许用户自定义重命名规则。我们可以参考以下步骤: 1. 确定工具功能:批量重命名指定目录下的文件,支持自定义命名规则(如前缀、后缀、序号、替换特定字符串等)。 2. 设计用户交互:可以是命令行工具,也可以是简单的图形界面(Swing或JavaFX)。考虑到简单性,我们先实现命令行版本。 3. 核心功能:遍历目录中的文件,根据规则生成新文件名,然后重命名。 开发步骤: 1. 读取用户输入的目录路径。 2. 读取用户指定的重命名规则(例如:添加前缀、后缀、替换字符串、使用序号等)。 3. 遍历目录中的每个文件(可能需要过滤目录或特定文件)。 4. 根据规则生成新文件名。 5. 执行重命名操作(注意:避免文件名冲突,可能需要临时策略)。 代码结构: - 主类:BatchRenameTool - 方法:main(入口),renameFiles(执行重命名),generateNewName(根据规则生成新文件名) 引用中提到的注意事项: - 企业内部部署需要测试和培训(但我们目前是简单工具,可先不考虑) - 命名规则制定:我们将支持基本的规则,如添加前缀、后缀、序号、替换字符串等。 根据引用[4],我们知道可以使用正则表达式来匹配文件名并进行替换,因此我们可以考虑支持正则表达式替换。 规则设计: 1. 添加前缀:prefix + 原文件名 2. 添加后缀:原文件名 + suffix 3. 替换字符串:将原文件名中的某个字符串替换为另一个字符串(支持普通字符串替换和正则表达式替换) 4. 序号:在文件名中插入序号(可以指定起始序号、增量、位数等) 我们还可以考虑更复杂的规则,但先实现基本规则。 命令行参数设计: 我们可以通过命令行参数来指定目录和规则,例如: java BatchRenameTool -d "目录路径" -rule "规则类型:参数" 但这样可能比较复杂,我们可以采用交互式命令行(即运行时输入)。 为了简单,我们先实现一个非交互式,通过程序参数传递。 或者,我们也可以使用属性文件来配置规则,但用户要求简单工具,所以交互式命令行可能更友好。 我们分步实现,先实现一个固定规则的,然后逐步扩展。 第一步:实现一个简单的替换字符串的功能。 示例代码框架: ```java import java.io.File; public class BatchRenameTool { public static void main(String[] args) { // 检查参数 if (args.length < 3) { System.out.println("用法: java BatchRenameTool <目录> <旧字符串> <新字符串>"); return; } String dirPath = args[0]; String oldStr = args[1]; String newStr = args[2]; File dir = new File(dirPath); if (!dir.isDirectory()) { System.out.println("错误: " + dirPath + " 不是一个目录。"); return; } File[] files = dir.listFiles(); if (files == null) { System.out.println("无法读取目录中的文件。"); return; } for (File file : files) { if (file.isFile()) { // 只重命名文件,忽略目录 String oldName = file.getName(); String newName = oldName.replace(oldStr, newStr); if (!oldName.equals(newName)) { File newFile = new File(dir, newName); // 检查新文件名是否已存在 if (newFile.exists()) { System.out.println("警告: 文件 " + newName + " 已存在,跳过重命名 " + oldName); } else { if (file.renameTo(newFile)) { System.out.println("重命名成功: " + oldName + " -> " + newName); } else { System.out.println("重命名失败: " + oldName); } } } } } } } ``` 以上代码实现了简单的字符串替换。但存在一些问题: 1. 没有处理文件名冲突(当多个文件替换后重名时) 2. 只支持简单的字符串替换 改进方向: 1. 支持更多规则(如添加前缀、后缀、序号) 2. 支持正则表达式替换 3. 处理文件名冲突(例如,在重命名时,如果目标文件已存在,则自动在文件名后加数字序号) 接下来,我们扩展功能,支持多种规则。我们可以设计一个规则配置类,然后通过解析用户输入来构建规则。 由于时间有限,我们设计支持以下规则(通过命令行参数指定): - 替换:-replace oldStr newStr - 添加前缀:-prefix prefixStr - 添加后缀:-suffix suffixStr - 使用序号:-seq [起始值] [增量] [位数] [位置](位置可以是prefix或suffix,表示序号放在前面还是后面) 但命令行参数解析会变得复杂,我们可以使用Apache Commons CLI等库,但为了简单,我们使用简单解析。 或者,我们可以让用户输入一个规则字符串,然后解析。例如: 规则字符串:"prefix=ABC,suffix=XYZ,seq=1,2,3,prefix" 表示添加前缀ABC,后缀XYZ,并且添加前缀序号(从1开始,每次加2,位数3位,放在前缀位置) 但这样还是复杂。我们分步骤实现,先支持一种规则,然后逐步扩展。 另一种思路:使用配置文件(JSON或Properties),但用户要求简单工具,我们先实现一个交互式命令行工具,逐步提示用户输入规则。 由于用户要求“简单的工具”,我们可以先实现一个交互式版本,让用户输入目录,然后选择规则类型,再输入规则参数。 下面我们改为交互式命令行版本: ```java import java.io.File; import java.util.Scanner; public class BatchRenameTool { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("请输入目录路径: "); String dirPath = scanner.nextLine().trim(); File dir = new File(dirPath); if (!dir.isDirectory()) { System.out.println("错误: 目录不存在或不是一个目录。"); return; } System.out.println("请选择重命名规则:"); System.out.println("1. 替换字符串"); System.out.println("2. 添加前缀"); System.out.println("3. 添加后缀"); System.out.println("4. 添加序号"); System.out.print("请输入选项(1-4): "); int choice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 File[] files = dir.listFiles(); if (files == null) { System.out.println("无法读取目录中的文件。"); return; } switch (choice) { case 1: System.out.print("请输入要替换的字符串: "); String oldStr = scanner.nextLine(); System.out.print("请输入替换后的字符串: "); String newStr = scanner.nextLine(); renameByReplace(files, oldStr, newStr); break; case 2: System.out.print("请输入要添加的前缀: "); String prefix = scanner.nextLine(); renameByPrefix(files, prefix); break; case 3: System.out.print("请输入要添加的后缀: "); String suffix = scanner.nextLine(); renameBySuffix(files, suffix); break; case 4: System.out.print("请输入序号起始值: "); int start = scanner.nextInt(); System.out.print("请输入序号增量: "); int step = scanner.nextInt(); System.out.print("请输入序号位数(不足补0): "); int digits = scanner.nextInt(); System.out.print("序号位置(1.前缀 2.后缀): "); int seqPosChoice = scanner.nextInt(); boolean seqAsPrefix = (seqPosChoice == 1); renameBySequence(files, start, step, digits, seqAsPrefix); break; default: System.out.println("无效选项。"); } } private static void renameByReplace(File[] files, String oldStr, String newStr) { for (File file : files) { if (file.isFile()) { String oldName = file.getName(); String newName = oldName.replace(oldStr, newStr); renameFile(file, newName); } } } private static void renameByPrefix(File[] files, String prefix) { for (File file : files) { if (file.isFile()) { String oldName = file.getName(); String newName = prefix + oldName; renameFile(file, newName); } } } private static void renameBySuffix(File[] files, String suffix) { for (File file : files) { if (file.isFile()) { String oldName = file.getName(); // 在文件名和后缀之间插入后缀?还是在整个文件名后面? // 用户可能希望在后缀之前(扩展名之前)添加,或者在整个文件名之后(包括扩展名)? // 根据常见需求,我们通常是在主文件名后面添加,扩展名之前。但是这里我们简单处理:在整个文件名后面添加(包括扩展名后面) // 但这样可能不符合习惯,例如:file.txt -> file.txt_suffix 而不是 file_suffix.txt // 所以我们需要分割文件名和扩展名 int dotIndex = oldName.lastIndexOf('.'); if (dotIndex != -1) { String baseName = oldName.substring(0, dotIndex); String extension = oldName.substring(dotIndex); String newName = baseName + suffix + extension; renameFile(file, newName); } else { String newName = oldName + suffix; renameFile(file, newName); } } } } private static void renameBySequence(File[] files, int start, int step, int digits, boolean asPrefix) { int current = start; for (File file : files) { if (file.isFile()) { String oldName = file.getName(); String seq = String.format("%0" + digits + "d", current); current += step; String newName; if (asPrefix) { newName = seq + oldName; } else { // 后缀:同样要考虑扩展名 int dotIndex = oldName.lastIndexOf('.'); if (dotIndex != -1) { String baseName = oldName.substring(0, dotIndex); String extension = oldName.substring(dotIndex); newName = baseName + seq + extension; } else { newName = oldName + seq; } } renameFile(file, newName); } } } private static void renameFile(File file, String newName) { File newFile = new File(file.getParent(), newName); if (newFile.exists()) { System.out.println("警告: 文件 " + newName + " 已存在,跳过重命名 " + file.getName()); } else { if (file.renameTo(newFile)) { System.out.println("重命名成功: " + file.getName() + " -> " + newName); } else { System.out.println("重命名失败: " + file.getName()); } } } } ``` 注意:在添加后缀时,我们考虑了文件扩展名,将后缀添加在主文件名和扩展名之间。例如:file.txt添加后缀“_back”后变成file_back.txt。 这个程序已经可以实现基本功能。但还可以改进: 1. 支持组合规则(例如同时添加前缀和序号) 2. 支持正则表达式替换(在替换字符串时,可以使用正则表达式) 由于时间关系,我们先实现这个基本版本。用户可以根据需要扩展。 另外,重命名操作有风险,建议在重命名之前先列出将要重命名的文件,让用户确认。我们可以增加一个预览功能。 由于用户要求简单工具,我们暂不实现预览,但实际应用中建议添加。 最后,我们生成相关问题。</think>### Java 实现批量文件重命名工具开发指南 #### 1. 核心功能设计 - **文件遍历**:递归扫描指定目录下的所有文件 - **命名规则引擎**:支持多种重命名规则: - 添加前缀/后缀:`$prefix + 原文件名 + $suffix` - 序号生成:`文件_001.txt`, `文件_002.txt` - 字符串替换:`旧字符串 → 新字符串` - 正则表达式替换:`$filename.replaceAll(regex, replacement)` - 大小写转换:全大写/全小写/首字母大写 - **扩展名处理**:保留或统一修改文件扩展名 - **冲突解决**:自动处理重名文件(追加序号) #### 2. 开发步骤 ##### (1) 创建文件遍历模块 ```java import java.io.File; import java.util.ArrayList; import java.util.List; public class FileScanner { public static List<File> scanDirectory(String path) { List<File> fileList = new ArrayList<>(); File root = new File(path); if (!root.exists() || !root.isDirectory()) { throw new IllegalArgumentException("无效目录路径: " + path); } scanRecursive(root, fileList); return fileList; } private static void scanRecursive(File dir, List<File> fileList) { for (File file : dir.listFiles()) { if (file.isDirectory()) { scanRecursive(file, fileList); } else { fileList.add(file); } } } } ``` ##### (2) 命名规则引擎实现 ```java import java.util.regex.Pattern; public class RenameRuleEngine { public static String generateNewName(String original, RenameRule rule) { String newName = original; // 应用字符串替换 if (rule.getReplaceFrom() != null && rule.getReplaceTo() != null) { newName = newName.replace(rule.getReplaceFrom(), rule.getReplaceTo()); } // 应用正则表达式替换 if (rule.getRegexPattern() != null && rule.getRegexReplacement() != null) { newName = Pattern.compile(rule.getRegexPattern()) .matcher(newName) .replaceAll(rule.getRegexReplacement()); } // 添加前缀/后缀 if (rule.getPrefix() != null) newName = rule.getPrefix() + newName; if (rule.getSuffix() != null) { int dotIndex = newName.lastIndexOf('.'); if (dotIndex > 0) { newName = newName.substring(0, dotIndex) + rule.getSuffix() + newName.substring(dotIndex); } else { newName += rule.getSuffix(); } } // 大小写转换 switch (rule.getCaseType()) { case UPPER: newName = newName.toUpperCase(); break; case LOWER: newName = newName.toLowerCase(); break; case CAMEL: newName = toCamelCase(newName); break; } return newName; } private static String toCamelCase(String str) { // 实现首字母大写逻辑 } } // 规则配置类 public class RenameRule { private String prefix; private String suffix; private String replaceFrom; private String replaceTo; private String regexPattern; private String regexReplacement; private CaseType caseType; // 枚举定义 public enum CaseType { NONE, UPPER, LOWER, CAMEL } // getters & setters } ``` ##### (3) 批量重命名执行器 ```java import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.List; public class BatchRenamer { public static void renameFiles(List<File> files, RenameRule rule) throws IOException { for (File file : files) { String newName = RenameRuleEngine.generateNewName( file.getName(), rule ); Path source = file.toPath(); Path target = source.resolveSibling(newName); // 处理文件名冲突 int counter = 1; while (Files.exists(target)) { String baseName = newName.substring(0, newName.lastIndexOf('.')); String ext = newName.substring(newName.lastIndexOf('.')); newName = baseName + "_" + counter + ext; target = source.resolveSibling(newName); counter++; } Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); } } } ``` ##### (4) 主程序入口 ```java import java.util.List; import java.util.Scanner; public class RenameTool { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("输入目录路径: "); String dirPath = scanner.nextLine(); System.out.print("命名规则选择:\n1.添加前缀\n2.添加后缀\n3.替换文本\n4.正则替换\n> "); int choice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 RenameRule rule = new RenameRule(); // 根据用户输入配置规则... try { List<File> files = FileScanner.scanDirectory(dirPath); BatchRenamer.renameFiles(files, rule); System.out.println("成功重命名 " + files.size() + " 个文件"); } catch (Exception e) { System.err.println("错误: " + e.getMessage()); } } } ``` #### 3. 企业级功能增强 1. **配置文件支持**:使用XML/JSON保存规则配置 ```xml <rename-config> <prefix>项目A_</prefix> <replace from="临时" to="正式"/> <regex pattern="\d{4}" replacement="2023"/> </rename-config> ``` 2. **日志记录**:添加操作日志和回滚功能 3. **预览模式**:执行前显示重命名结果预览 4. **过滤器扩展**:按文件类型/大小/日期过滤 ```java public static List<File> filterByExtension(List<File> files, String ext) { return files.stream() .filter(f -> f.getName().endsWith(ext)) .collect(Collectors.toList()); } ``` #### 4. 安全注意事项 1. **权限验证**: ```java if (!Files.isWritable(file.toPath())) { throw new SecurityException("无写权限: " + file.getPath()); } ``` 2. **输入验证**:防范路径遍历攻击 ```java if (path.contains("..") || path.contains("/")) { throw new IllegalArgumentException("非法路径字符"); } ``` 3. **操作回滚**:实现事务性重命名 ```java Map<Path, Path> rollbackMap = new HashMap<>(); // 执行前保存原始路径 ``` #### 5. 测试方案 1. **单元测试**:验证命名规则引擎 ```java @Test void testPrefixAddition() { RenameRule rule = new RenameRule(); rule.setPrefix("IMG_"); assertEquals("IMG_cat.jpg", RenameRuleEngine.generateNewName("cat.jpg", rule)); } ``` 2. **集成测试**:模拟真实目录结构 3. **压力测试**:处理10,000+文件场景 ### 相关技术扩展 1. **Java NIO 优化**:使用`Files.walk`替代递归遍历 2. **多线程处理**:加速大批量文件操作 ```java ExecutorService executor = Executors.newFixedThreadPool(8); files.forEach(f -> executor.submit(() -> renameFile(f, rule))); ``` 3. **GUI 集成**:通过Swing/JavaFX创建可视化界面
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值