You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现Windows文件名保留字符替换及原文件名还原方案咨询

解决Windows文件名非法字符替换与还原的Java方案

这里有两种可靠的实现思路,既能替换掉Windows不允许的:?|'<>*!"这些字符,又能后续完美还原回原文件名,完全避开单一字符替换的局限:

方案一:自定义唯一字符串映射(可读性优先)

这个方案给每个非法字符分配一个唯一的、合法的字符串占位符(比如用__COLON__代替:),替换和还原都基于双向映射表来实现,优点是替换后的文件名可读性强,能一眼看出原字符是什么。

代码实现

import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class FileNameHandler {
    // 定义Windows禁止的文件名字符集合
    private static final Set<Character> INVALID_WINDOWS_CHARS = Set.of(':', '?', '|', '\'', '<', '>', '*', '!', '"');
    
    // 正向映射:非法字符 → 唯一合法替换字符串
    private static final Map<Character, String> CHAR_TO_REPLACEMENT = Map.of(
            ':', "__COLON__",
            '?', "__QUESTION__",
            '|', "__PIPE__",
            '\'', "__APOSTROPHE__",
            '<', "__LESS_THAN__",
            '>', "__GREATER_THAN__",
            '*', "__ASTERISK__",
            '!', "__EXCLAMATION__",
            '"', "__QUOTATION__"
    );
    
    // 反向映射:替换字符串 → 原非法字符(用于还原)
    private static final Map<String, Character> REPLACEMENT_TO_CHAR = CHAR_TO_REPLACEMENT.entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

    // 替换非法字符,生成合法文件名
    public static String sanitizeFileName(String originalFileName) {
        StringBuilder sb = new StringBuilder();
        for (char c : originalFileName.toCharArray()) {
            if (INVALID_WINDOWS_CHARS.contains(c)) {
                sb.append(CHAR_TO_REPLACEMENT.get(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    // 将替换后的文件名还原回原文件名
    public static String restoreFileName(String sanitizedFileName) {
        StringBuilder sb = new StringBuilder();
        int i = 0;
        int len = sanitizedFileName.length();
        
        while (i < len) {
            // 检查是否匹配我们定义的替换字符串前缀__
            if (i + 2 <= len && sanitizedFileName.startsWith("__", i)) {
                int endIdx = sanitizedFileName.indexOf("__", i + 2);
                if (endIdx != -1) {
                    String replacementStr = sanitizedFileName.substring(i, endIdx + 2);
                    Character originalChar = REPLACEMENT_TO_CHAR.get(replacementStr);
                    if (originalChar != null) {
                        sb.append(originalChar);
                        i = endIdx + 2;
                        continue;
                    }
                }
            }
            // 不匹配替换规则时,直接追加当前字符
            sb.append(sanitizedFileName.charAt(i));
            i++;
        }
        return sb.toString();
    }

    // 测试示例
    public static void main(String[] args) {
        String original = "my:file?name|'<>*!\".txt";
        String sanitized = sanitizeFileName(original);
        String restored = restoreFileName(sanitized);
        
        System.out.println("原文件名: " + original);
        System.out.println("合法文件名: " + sanitized);
        System.out.println("还原后文件名: " + restored);
        // 输出:还原后文件名与原文件名完全一致
    }
}

注意事项

  • 替换字符串的格式(比如__XXX__)要确保不会出现在原合法文件名中,否则还原时会出错。如果担心冲突,可以把前缀改得更独特,比如加上项目专属标识(如__MYAPP_COLON__)。

方案二:Unicode私有区域字符映射(无冲突优先)

如果不想担心替换字符串和原文件名冲突,可以用Unicode的**私有使用区域(Private Use Area)**字符——这些字符是专门留给应用自定义使用的,不会出现在普通文本中,替换和还原逻辑更简单,还能保持文件名长度不变。

代码实现

import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class FileNameHandler {
    private static final Set<Character> INVALID_WINDOWS_CHARS = Set.of(':', '?', '|', '\'', '<', '>', '*', '!', '"');
    
    // 正向映射:非法字符 → Unicode私有区域字符(选\uE000-\uF8FF范围内的字符)
    private static final Map<Character, Character> CHAR_TO_PRIVATE = Map.of(
            ':', '\uE000',
            '?', '\uE001',
            '|', '\uE002',
            '\'', '\uE003',
            '<', '\uE004',
            '>', '\uE005',
            '*', '\uE006',
            '!', '\uE007',
            '"', '\uE008'
    );
    
    // 反向映射:私有区域字符 → 原非法字符
    private static final Map<Character, Character> PRIVATE_TO_CHAR = CHAR_TO_PRIVATE.entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

    public static String sanitizeFileName(String originalFileName) {
        StringBuilder sb = new StringBuilder();
        for (char c : originalFileName.toCharArray()) {
            // 存在映射则替换,否则保留原字符
            sb.append(CHAR_TO_PRIVATE.getOrDefault(c, c));
        }
        return sb.toString();
    }

    public static String restoreFileName(String sanitizedFileName) {
        StringBuilder sb = new StringBuilder();
        for (char c : sanitizedFileName.toCharArray()) {
            sb.append(PRIVATE_TO_CHAR.getOrDefault(c, c));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String original = "my:file?name|'<>*!\".txt";
        String sanitized = sanitizeFileName(original);
        String restored = restoreFileName(sanitized);
        
        System.out.println("原文件名: " + original);
        System.out.println("合法文件名: " + sanitized); // 显示可能是方块,但文件操作完全正常
        System.out.println("还原后文件名: " + restored);
    }
}

优缺点

  • 优点:完全避免和原文件名的字符冲突,替换/还原逻辑更简洁高效,文件名长度不变。
  • 缺点:私有区域字符在部分字体下会显示为方块(□),但不影响文件的创建、读取等操作,还原后完全正常。

内容的提问来源于stack exchange,提问作者FunWithJava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:57:09