4. 检查并修复A2L文件中的非法字符

任务说明

A2L文件本质上是文本文件,但解析器通常要求其内容严格限定在ASCII字符集内,且字符串字面量中的转义序列必须合法。当文件中混入全角空格、中文标点、不可见控制字符或类似 \xZZ 的非法转义时,解析器会在词法分析阶段直接报错或产生乱码。本案例的目标是编写一个Python脚本,扫描指定A2L文件,定位所有非ASCII字符及非法转义序列,并自动替换为合法ASCII字符,输出修复后的文件供解析器读取。

实现思路

脚本按行读取A2L文件,对每一行执行两类检查:

  1. 非ASCII字符:将行编码为ASCII时失败的字符即为非法字符。根据常见错误类型做映射替换(如全角逗号→半角逗号,中文引号→英文引号),无法映射的字符统一替换为下划线。
  2. 非法转义序列:在双引号字符串内查找 \x 后跟非十六进制字符的组合,以及孤立的 \ 后跟非 \\、\" 等合法转义的情况,将其中的反斜杠替换为正斜杠或删除。

修复完成后,脚本会报告每处替换的位置和内容,并将结果写入新文件。

完整代码

import re
import sys

# 常见非ASCII字符的映射表
CHAR_MAP = {
    '\u3000': ' ',   # 全角空格
    '\uff0c': ',',   # 全角逗号
    '\u3002': '.',   # 全角句号
    '\uff08': '(',   # 全角左括号
    '\uff09': ')',   # 全角右括号
    '\u201c': '"',   # 左双引号
    '\u201d': '"',   # 右双引号
    '\u2018': "'",   # 左单引号
    '\u2019': "'",   # 右单引号
    '\uff1a': ':',   # 全角冒号
    '\uff1b': ';',   # 全角分号
}

def fix_non_ascii(line, line_no, report):
    """替换非ASCII字符,返回修复后的行"""
    result = []
    for ch in line:
        if ord(ch) > 127:
            if ch in CHAR_MAP:
                result.append(CHAR_MAP[ch])
                report.append(f"行{line_no}: 非ASCII字符 U+{ord(ch):04X} ({ch}) → {CHAR_MAP[ch]}")
            else:
                result.append('_')
                report.append(f"行{line_no}: 非ASCII字符 U+{ord(ch):04X} ({ch}) → _")
        else:
            result.append(ch)
    return ''.join(result)

def fix_bad_escapes(line, line_no, report):
    """修复字符串中的非法转义序列"""
    # 匹配 \x 后跟非十六进制字符的情况
    line = re.sub(r'\\x(?![0-9A-Fa-f]{2})', '/x', line)
    # 匹配孤立反斜杠(后跟非合法转义字符)
    line = re.sub(r'\\(?![\\"nrt])', '/', line)
    return line

def process_file(input_path, output_path):
    report = []
    fixed_lines = []
    
    with open(input_path, 'r', encoding='utf-8') as f:
        for line_no, line in enumerate(f, 1):
            original = line
            # 先处理非ASCII字符
            line = fix_non_ascii(line, line_no, report)
            # 再处理非法转义
            line = fix_bad_escapes(line, line_no, report)
            if line != original:
                report.append(f"行{line_no}: 内容已修改")
            fixed_lines.append(line)
    
    with open(output_path, 'w', encoding='ascii') as f:
        f.writelines(fixed_lines)
    
    return report

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("用法: python fix_a2l_chars.py <输入文件.a2l> <输出文件.a2l>")
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    
    changes = process_file(input_file, output_file)
    
    if changes:
        print(f"共修复 {len(changes)} 处问题:")
        for item in changes:
            print(f"  {item}")
    else:
        print("未发现非法字符,文件已原样复制。")
    print(f"输出文件: {output_file}")

运行与验证

运行方式:

python fix_a2l_chars.py 原始文件.a2l 修复后文件.a2l

验证步骤:

  1. 准备一个包含非法字符的测试A2L文件,例如在 MEASUREMENT 块描述中混入全角逗号和中文引号,在注释字符串中加入 \xZZ。
  2. 运行上述脚本,观察输出报告是否列出了所有非法字符的位置和替换结果。
  3. 检查输出文件:用十六进制编辑器或 file 命令确认输出文件为纯ASCII编码(无UTF-8多字节序列)。
  4. 用A2L解析器(如INCA或第三方库 a2lparser)尝试解析输出文件,确认不再报词法错误。

通过条件:脚本报告的所有替换位置与文件中实际存在的非法字符一一对应;输出文件可通过 python -c "open('修复后文件.a2l','rb').read().decode('ascii')" 验证无解码异常;解析器能成功读取修复后的文件。

常见问题

  • 误替换合法反斜杠:A2L中路径字符串可能包含 \\ 表示单个反斜杠,正则已排除 \\ 和 \" 等合法转义,不会误伤。
  • 非ASCII字符出现在注释中:脚本不区分注释与代码,统一替换。若注释中的非ASCII字符不影响解析,可手动保留,但本脚本为通用安全起见一律替换。
  • 文件编码:输入文件需为UTF-8编码。若原始文件为GBK等编码,请先转换为UTF-8再运行脚本。