创作历史
 
 
前言
 
 
例子
 
>> P+O2=P2O5
4P+5O2=2P2O5
>> H2O(g)+Fe=Fe3O4+H2
4H2O(g)+3Fe=Fe3O4+4H2
>> CO+Fe2O3=Fe+CO2
3CO+Fe2O3=2Fe+3CO2
>> C2H5OH+O2=CO2+H2O
C2H5OH+3O2=2CO2+3H2O
>> Cl2+<e->=Cl<e->
Cl2+2<e->=2Cl<e->
>> Cu+Fe<3e+>=Cu<2e+>+Fe<2e+>
Cu+2Fe<3e+>=Cu<2e+>+2Fe<2e+>
>> LiOH+H2O2+H2O=Li2O2.H2O2.3H2O
2LiOH+2H2O2+H2O=Li2O2.H2O2.3H2O
>> C{n}H{2n+2}+O2=CO2+H2O
{(n+1)^(-1)}C{n}H{2*n+2}+{(1/2)*(3*n+1)/(n+1)}O2={n/(n+1)}CO2+H2O
>> CH3(CHCH){n}CH3+Cl2=CH3(CHClCHCl){n}CH3
CH3(CHCH){n}CH3+{n}Cl2=CH3(CHClCHCl){n}CH3
>> X-<e->=X<{n}e+>
X-{n}<e->=X<{n}e+>
>> CH4;HCN;NH3;O2;H2O
2HCN+6H2O=2CH4+2NH3+3O2
>> CH4(g);HCN(g);NH3(g);O2(g);H2O(g)
2CH4(g)+2NH3(g)+3O2(g)=2HCN(g)+6H2O(g)
 
运行截图
 
bce-console.exe运行结果(pip install bce安装)
 

 
总程序运行结果
 

 
需要的库
 
import re  
import string  
from sympy import solve, symbols  
 
步骤
 
 
拆分等式左右侧
 
 
if '=' not in ChemicalEquations and ';' in ChemicalEquations:
    all_items = ChemicalEquations.split(';')
    pass  
elif 'e+>' in ChemicalEquations or 'e->' in ChemicalEquations or '{' in ChemicalEquations or '.' in ChemicalEquations:
    pass  
else:
    left, right = ChemicalEquations.split('=')
 
拆分每个化学物质和±号
 
化学物质组成
 
 
1、倍数元素:\d|{[A-Za-z0-9+-]}
2、电荷元素:<(?:\d|{[A-Za-z0-9+-]})?e[+-]>
3、化学元素:[A-Z][a-z]?\.?
4、物质状态:\([gls]\)  
5、化学物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)
6、[②]物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:[A-Za-z0-9.]+(?:\([A-Za-z0-9]+\))?(?:{[A-Za-z0-9+-]+})?(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)
 
化学物质提取
 
left, right = ChemicalEquations.split('=')
items = re.compile(R'[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)')
left_items, right_items = items.findall(left), items.findall(right)
 
 
get_all = lambda _, __ = eval("[]"): __.clear() or [[__.append(____) for ____ in ___ if ____] for ___ in _] and __
 
 
统计每个化学物质内每个化学元素的数量
 
判断等式双方元素种类是否一致
 
 
ELEMENT = re.compile(R'([A-Z][a-z]?\.?|<(?:\d|{[A-Za-z0-9+-]})?e[+-]>)')
elements = list(set(get_all([ELEMENT.findall(item) for item in left_item_list if item not in list('+-')])))
right_elements = list(set(get_all([ELEMENT.findall(item) for item in right_item_list if item not in list('+-')])))
assert set(elements) == set(right_elements), '化学方程式两方元素不守恒!'
 
计算左右双方元素数量,获取每个元素在每个物质中的占比
 
 
scale_group = {element: {} for element in elements}
left_item_dict = {item: {} for item in left_item_list if item not in list('+-')}
right_item_dict = {item: {} for item in right_item_list if item not in list('+-')}
for item_list, item_dict in ((left_item_list, left_item_dict), (right_item_list, right_item_dict)):
    for item in item_list:
        if item not in list('+-'):
            for element in re.compile(R'([A-Z][a-z]?)(\d*)').findall(item):
                if element[0] not in item_dict[item]:
                    item_dict[item][element[0]] = 0
                item_dict[item][element[0]] += element[1].isdigit() and int(element[1]) or 1
for element in elements:
    for item_dict in (left_item_dict, right_item_dict):
        for item in item_dict:
            if element in item:
                scale_group[element][item] = item_dict[item][element]
 
解化学元素守恒方程
 
按物质名称创建符号变量
 
symbols_list = symbols([item for item in (left_item_list + right_item_list) if item not in list('+-')], positive=True, integer=True, real=True)  
for symbol in symbols_list:
    globals()[str(symbol)] = symbol  
 
根据左右元素总量关系创建关系式
 
 
solve_list = []
for element in elements:
    temp_str = ''
    temp = []
    for item in left_item_dict:
        index = scale_group[element].get(str(item), 
        if index:
            temp.append(f'{index}*{str(item)}')
    temp_str += '+'.join(temp)
    temp = []
    for item in right_item_dict:
        index = scale_group[element].get(str(item), 
        if index:
            temp.append(f'{index}*{str(item)}')
    if temp:
        temp_str += '-' + '-'.join(temp)
    solve_list.append(eval(temp_str))
 
求解各个化学物质之间的比例关系
 
 
res = solve(solve_list, symbols_list)
can_zhao = [item for item in symbols_list if str(item) == list(set(list(left_item_dict.keys()) + list(right_item_dict.keys())) - set(list(map(str, res.keys()))))[0]][0]
for item in res:
    res[item] /= can_zhao
res[can_zhao] = 1
fen_mu = re.compile(R'/(\d+)?').findall(str(res))
if fen_mu:
    bs = int(fen_mu[0])
    for item in res:
        res[item] *= bs
 
取比例组最小公倍数为化学物质系数
 
 
chemical_equations = []
for item_dict in (left_item_dict, right_item_dict):
    all_item = []
    all_index = []
    for item in item_dict:
        all_item.append(item)
        all_index.append(res[[i for i in symbols_list if str(i) == item][0]])
    chemical_equations.append(' + '.join([f'{"" if all_index[i] == 1 else all_index[i]}{all_item[i]}' for i in range(len(item_dict))]))
chemical_equations = ' = '.join(chemical_equations)
 
总代码
 
import re
import string
from sympy import solve, symbols
"""
逻辑流程:
1、拆分等式左右侧
2、拆分每个化学物质和+-号
3、统计每个化学物质内每个化学元素的数量
4、解化学元素守恒方程
5、取比例组最小公倍数为化学物质系数
元素特征:
1、倍数元素:\d|{[A-Za-z0-9+-]}
2、电荷元素:<(?:\d|{[A-Za-z0-9+-]})?e[+-]>
3、化学元素:[A-Z][a-z]?\.?
4、物质状态:\([gls]\)  # g是气体,l是液体,s是固体
5、化学物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)
6、[②]物质:[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:[A-Za-z0-9.]+(?:\([A-Za-z0-9]+\))?(?:{[A-Za-z0-9+-]+})?(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)
试验过程:
>> P+O2=P2O5
4P+5O2=2P2O5
>> H2O(g)+Fe=Fe3O4+H2
4H2O(g)+3Fe=Fe3O4+4H2
>> CO+Fe2O3=Fe+CO2
3CO+Fe2O3=2Fe+3CO2
>> C2H5OH+O2=CO2+H2O
C2H5OH+3O2=2CO2+3H2O
>> Cl2+<e->=Cl<e->
Cl2+2<e->=2Cl<e->
>> Cu+Fe<3e+>=Cu<2e+>+Fe<2e+>
Cu+2Fe<3e+>=Cu<2e+>+2Fe<2e+>
>> LiOH+H2O2+H2O=Li2O2.H2O2.3H2O
2LiOH+2H2O2+H2O=Li2O2.H2O2.3H2O
>> C{n}H{2n+2}+O2=CO2+H2O
{(n+1)^(-1)}C{n}H{2*n+2}+{(1/2)*(3*n+1)/(n+1)}O2={n/(n+1)}CO2+H2O
>> CH3(CHCH){n}CH3+Cl2=CH3(CHClCHCl){n}CH3
CH3(CHCH){n}CH3+{n}Cl2=CH3(CHClCHCl){n}CH3
>> X-<e->=X<{n}e+>
X-{n}<e->=X<{n}e+>
>> CH4;HCN;NH3;O2;H2O
2HCN+6H2O=2CH4+2NH3+3O2
>> CH4(g);HCN(g);NH3(g);O2(g);H2O(g)
2CH4(g)+2NH3(g)+3O2(g)=2HCN(g)+6H2O(g)
"""
ChemicalEquationsList = [
    'P+O2=P2O5',                                
    'H2O(g)+Fe=Fe3O4+H2',                       
    'CO+Fe2O3=Fe+CO2',                          
    'C2H5OH+O2=CO2+H2O',                        
    'Cl2+<e->=Cl<e->',                          
    'Cu+Fe<3e+>=Cu<2e+>+Fe<2e+>',               
    'LiOH+H2O2+H2O=Li2O2.H2O2.3H2O',            
    'C{n}H{2n+2}+O2=CO2+H2O',                   
    'CH3(CHCH){n}CH3+Cl2=CH3(CHClCHCl){n}CH3',  
    'X-<e->=X<{n}e+>',                          
    'CH4;HCN;NH3;O2;H2O',                       
    'CH4(g);HCN(g);NH3(g);O2(g);H2O(g)',        
]
ELEMENT = re.compile(R'([A-Z][a-z]?|<(?:\d|{[A-Za-z0-9+-]})?e[+-]>)')
def max_gys(num1, num2):  
    while num2:
        temp = num1 % num2
        num1, num2 = num2, temp
    return num1
def min_gbs(num1, num2):  
    return num1 * num2 // max_gys(num1, num2)
get_all = lambda _, __ = eval("[]"): __.clear() or [[__.append(____) for ____ in ___ if ____] for ___ in _] and __
def handle_double_elements_list_scale(element1, element2, chemicals, scale_group):  
    shared = list(set(chemicals[element1]) & set(chemicals[element2]))
    if shared:
        multiply = min_gbs(scale_group[element1][shared[0]], scale_group[element2][shared[0]])
        shared1 = scale_group[element1][shared[0]]
        for item in scale_group[element1]:
            scale_group[element1][item] *= (multiply // shared1)
        shared2 = scale_group[element2][shared[0]]
        for item in scale_group[element2]:
            scale_group[element2][item] *= (multiply // shared2)
    return element2
def calc_equations(left_item_list, right_item_list):
    elements = list(set(get_all([ELEMENT.findall(item) for item in left_item_list if item not in list('+-')])))
    right_elements = list(set(get_all([ELEMENT.findall(item) for item in right_item_list if item not in list('+-')])))
    assert set(elements) == set(right_elements), '化学方程式两方元素不守恒!'
    symbols_list = symbols([item for item in (left_item_list + right_item_list) if item not in list('+-')], positive=True, integer=True, real=True)  
    for symbol in symbols_list:
        globals()[str(symbol)] = symbol  
    scale_group = {element: {} for element in elements}
    left_item_dict = {item: {} for item in left_item_list if item not in list('+-')}
    right_item_dict = {item: {} for item in right_item_list if item not in list('+-')}
    for item_list, item_dict in ((left_item_list, left_item_dict), (right_item_list, right_item_dict)):
        for item in item_list:
            if item not in list('+-'):
                for element in re.compile(R'([A-Z][a-z]?)(\d*)').findall(item):
                    if element[0] not in item_dict[item]:
                        item_dict[item][element[0]] = 0
                    item_dict[item][element[0]] += element[1].isdigit() and int(element[1]) or 1
    for element in elements:
        for item_dict in (left_item_dict, right_item_dict):
            for item in item_dict:
                if element in item:
                    scale_group[element][item] = item_dict[item][element]
    solve_list = []
    for element in elements:
        temp_str = ''
        temp = []
        for item in left_item_dict:
            index = scale_group[element].get(str(item), "")
            if index:
                temp.append(f'{index}*{str(item)}')
        temp_str += '+'.join(temp)
        temp = []
        for item in right_item_dict:
            index = scale_group[element].get(str(item), "")
            if index:
                temp.append(f'{index}*{str(item)}')
        if temp:
            temp_str += '-' + '-'.join(temp)
        solve_list.append(eval(temp_str))
    res = solve(solve_list, symbols_list)
    can_zhao = [item for item in symbols_list if str(item) == list(set(list(left_item_dict.keys()) + list(right_item_dict.keys())) - set(list(map(str, res.keys()))))[0]][0]
    for item in res:
        res[item] /= can_zhao
    res[can_zhao] = 1
    fen_mu = re.compile(R'/(\d+)?').findall(str(res))
    if fen_mu:
        bs = int(fen_mu[0])
        for item in res:
            res[item] *= bs
    chemical_equations = []
    for item_dict in (left_item_dict, right_item_dict):
        all_item = []
        all_index = []
        for item in item_dict:
            all_item.append(item)
            all_index.append(res[[i for i in symbols_list if str(i) == item][0]])
        chemical_equations.append(' + '.join([f'{"" if all_index[i] == 1 else all_index[i]}{all_item[i]}' for i in range(len(item_dict))]))
    chemical_equations = ' = '.join(chemical_equations)
    return chemical_equations
for ChemicalEquations in ChemicalEquationsList:
    for space in string.whitespace:
        ChemicalEquations = ChemicalEquations.replace(space, '')
    if '=' not in ChemicalEquations and ';' in ChemicalEquations:
        all_items = ChemicalEquations.split(';')
        pass  
    elif 'e+>' in ChemicalEquations or 'e->' in ChemicalEquations or '{' in ChemicalEquations or '.' in ChemicalEquations:
        pass  
    else:
        left, right = ChemicalEquations.split('=')
        items = re.compile(R'[+-]?(<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>|(?:(?:\(?(?:[A-Z][a-z]?\.?)+(?:\d+|{[A-Za-z0-9+-]+})?\)?)+(?:<(?:\d+|{[A-Za-z0-9+-]+})?e[+-]>)?)+)(?:\([gls]\))?([+-]?)')
        left_items, right_items = items.findall(left), items.findall(right)
        print(ChemicalEquations + '\n\t' + calc_equations(list(get_all(left_items)), list(get_all(right_items))))
 
结束语