mdb_names.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. # -*- coding: utf-8 -*-
  2. """MDB 列名规则(单一实现):CSV 表头 → Access 可用的列名。
  3. 为什么要有这个模块:Access 的列名不能含 `.` `!` `[` `]` 等字符,建库时要把表头"消毒"成合法名;
  4. **摄入侧按列名取数**(契约里的列名是 CSV 原样名)⇒ 两边必须用**同一套规则**,否则从 MDB 取数会
  5. 静默少列(用户令 2026-09-19「scada 数据接入兼容支持 CSV、MDB」的必答题)。
  6. `scripts/csv_to_mdb.py`(写侧)与 `src/windscada/scada_source.py`(读侧)都从这里取同一实现。
  7. """
  8. from __future__ import annotations
  9. import re
  10. KEEP = re.compile(r'[^0-9A-Za-z_\u4e00-\u9fff]')
  11. def safe_name(col: str, maxlen: int = 60) -> str:
  12. """单个表头 → 合法列名(去掉非法字符、截断;长度上限 60 便于撞名时加后缀)。"""
  13. return KEEP.sub('_', str(col).lstrip('\ufeff'))[:maxlen] or 'c'
  14. def safe_names(cols, keep: int = 60) -> list[str]:
  15. """一串表头 → 合法且**互不重名**的列名(撞名加 `_1` `_2`…,与写侧逐一对齐)。"""
  16. out, seen = [], {}
  17. for i, c in enumerate(cols):
  18. n = safe_name(c, keep) or f'c{i}'
  19. if n in seen:
  20. seen[n] += 1
  21. n = f'{n[:keep - 4]}_{seen[n]}'
  22. else:
  23. seen[n] = 0
  24. out.append(n)
  25. return out
  26. def map_cols(union: list[str]) -> dict[str, str]:
  27. """源表头 → 库内列名(按 safe_names 的顺序规则算,用于读侧把契约列名翻成库内列名)。"""
  28. return dict(zip(union, safe_names(list(union))))
  29. def stored_to_source(union: list[str]) -> dict[str, str]:
  30. """库内列名 → 源表头(读侧取回后把名字改回来,让下游看到契约里的列名)。"""
  31. return {v: k for k, v in map_cols(union).items()}