127 lines
5.3 KiB
Python
127 lines
5.3 KiB
Python
import pandas as pd
|
||
import os
|
||
import re
|
||
|
||
|
||
def extract_cif_from_xlsx(
|
||
xlsx_path: str,
|
||
output_dir: str,
|
||
naming_mode: str = 'formula',
|
||
name_col: int = 0,
|
||
cif_col: int = 1,
|
||
prefix: str = 'wjy'
|
||
):
|
||
"""
|
||
从 XLSX 文件中提取 CIF 数据并保存为单独的 .cif 文件。
|
||
|
||
Args:
|
||
xlsx_path (str): 输入的 XLSX 文件的路径。
|
||
output_dir (str): 输出 .cif 文件的文件夹路径。
|
||
naming_mode (str, optional): CIF 文件的命名模式。
|
||
可选值为 'formula' (使用第一列的名字) 或
|
||
'auto' (使用前缀+自动递增编号)。
|
||
默认为 'formula'。
|
||
name_col (int, optional): 包含文件名的列的索引(从0开始)。默认为 0。
|
||
cif_col (int, optional): 包含 CIF 内容的列的索引(从0开始)。默认为 1。
|
||
prefix (str, optional): 在 'auto' 命名模式下使用的文件名前缀。默认为 'wjy'。
|
||
|
||
Raises:
|
||
FileNotFoundError: 如果指定的 xlsx_path 文件不存在。
|
||
ValueError: 如果指定的 naming_mode 无效。
|
||
Exception: 处理过程中发生的其他错误。
|
||
"""
|
||
# --- 1. 参数校验和准备 ---
|
||
if not os.path.exists(xlsx_path):
|
||
raise FileNotFoundError(f"错误: 输入文件未找到 -> {xlsx_path}")
|
||
|
||
if naming_mode not in ['formula', 'auto']:
|
||
raise ValueError(f"错误: 'naming_mode' 参数必须是 'formula' 或 'auto',但收到了 '{naming_mode}'")
|
||
|
||
# 创建输出目录(如果不存在)
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
print(f"CIF 文件将保存到: {output_dir}")
|
||
|
||
try:
|
||
# --- 2. 读取 XLSX 文件 ---
|
||
# header=None 表示第一行不是标题,将其作为数据读取
|
||
df = pd.read_excel(xlsx_path, header=None)
|
||
|
||
# 跳过原始文件的表头行('formula', 'cif')
|
||
if str(df.iloc[0, name_col]).strip().lower() == 'formula' and str(df.iloc[0, cif_col]).strip().lower() == 'cif':
|
||
df = df.iloc[1:]
|
||
print("检测到并跳过了表头行。")
|
||
|
||
# --- 3. 遍历数据并生成文件 ---
|
||
success_count = 0
|
||
for index, row in df.iterrows():
|
||
# 获取文件名和 CIF 内容
|
||
formula_name = str(row[name_col])
|
||
cif_content = str(row[cif_col])
|
||
|
||
# 跳过内容为空的行
|
||
if pd.isna(row[name_col]) or pd.isna(row[cif_col]) or not cif_content.strip():
|
||
print(f"警告: 第 {index + 2} 行数据不完整,已跳过。")
|
||
continue
|
||
|
||
# --- 4. 根据命名模式确定文件名 ---
|
||
if naming_mode == 'formula':
|
||
# 清理文件名,替换掉不适合做文件名的特殊字符
|
||
# 例如:将 (PO4)3 替换为 _PO4_3,将 / 替换为 _
|
||
safe_filename = re.sub(r'[\\/*?:"<>|()]', '_', formula_name)
|
||
filename = f"{safe_filename}.cif"
|
||
else: # naming_mode == 'auto'
|
||
# 使用 format 方法来确保编号格式统一,例如 001, 002
|
||
filename = f"{prefix}_{success_count + 1:03d}.cif"
|
||
|
||
# 构造完整的输出文件路径
|
||
output_path = os.path.join(output_dir, filename)
|
||
|
||
# --- 5. 写入 CIF 文件 ---
|
||
try:
|
||
with open(output_path, 'w', encoding='utf-8') as f:
|
||
f.write(cif_content)
|
||
success_count += 1
|
||
except IOError as e:
|
||
print(f"错误: 无法写入文件 {output_path}。原因: {e}")
|
||
|
||
print(f"\n处理完成!成功提取并生成了 {success_count} 个 CIF 文件。")
|
||
|
||
except Exception as e:
|
||
print(f"处理 XLSX 文件时发生错误: {e}")
|
||
|
||
|
||
# --- 函数使用示例 ---
|
||
if __name__ == '__main__':
|
||
# 假设您的 XLSX 文件名为 'materials.xlsx',且与此脚本在同一目录下
|
||
source_xlsx_file = 'input/cif_dataset.xlsx'
|
||
|
||
# 检查示例文件是否存在,如果不存在则创建一个
|
||
if not os.path.exists(source_xlsx_file):
|
||
print(f"未找到示例文件 '{source_xlsx_file}',正在创建一个...")
|
||
example_data = {
|
||
'formula': ['Li3Al0.3Ti1.7(PO4)3', 'Li6.5La3Zr1.75W0.25O12', 'Invalid/Name*Test'],
|
||
'cif': ['# CIF Data for Li3Al0.3...\n_atom_site_type_symbol\n Li\n Al\n Ti\n P\n O',
|
||
'# CIF Data for Li6.5La3...\n_symmetry_space_group_name_H-M \'I a -3 d\'',
|
||
'# CIF Data for Invalid Name Test']
|
||
}
|
||
pd.DataFrame(example_data).to_excel(source_xlsx_file, index=False, header=True)
|
||
print("示例文件创建成功。")
|
||
|
||
# --- 示例 1: 使用第一列的 'formula' 命名 ---
|
||
# print("\n--- 示例 1: 使用 'formula' 命名模式 ---")
|
||
# output_folder_1 = 'cif_by_formula'
|
||
# extract_cif_from_xlsx(
|
||
# xlsx_path=source_xlsx_file,
|
||
# output_dir=output_folder_1,
|
||
# naming_mode='formula'
|
||
# )
|
||
|
||
# --- 示例 2: 使用 'wjy+编号' 自动命名 ---
|
||
print("\n--- 示例 2: 使用 'auto' 命名模式 ---")
|
||
output_folder_2 = 'cif_by_auto'
|
||
extract_cif_from_xlsx(
|
||
xlsx_path=source_xlsx_file,
|
||
output_dir=output_folder_2,
|
||
naming_mode='auto',
|
||
prefix='wjy'
|
||
) |