如何遍历 2 个文件中的行,比较值并在满足条件时更新文件中的值?

Posted

技术标签:

【中文标题】如何遍历 2 个文件中的行,比较值并在满足条件时更新文件中的值?【英文标题】:How to iterate over the rows from 2 files, compare the values and update the value in a file when the condition is met? 【发布时间】:2021-11-26 15:51:45 【问题描述】:

为了将值从 10 更改为 18、19 或 20,我正在拆分字符串,访问子字符串,然后尝试更改它。它的工作,但只是不改变价值观。这是我正在尝试实施的解决方案:

oldFileName = 'tryout.hmo'
newFileName = 'tryout_NEW.hmo'
topoFileName = 'Density.topo'

readme = open( oldFileName, "r" )
oldLines = readme.readlines()       

readme = open(topoFileName, "r")
Lines = readme.readlines()
readme.close()

newFile = open(newFileName,"w")


for row in oldLines:
    for line in Lines:
        tmp = line.split()
    list =  row.rstrip()
    tmp1 = list.split()
    newFile.write(row)
    if row.find("BEG_ELEM_DATA") > -1:
       if tmp[0] == tmp1[0]:
           if tmp[2] == 1 and tmp[3] == 0:
            # it is magnet, value 18
             newFile.write(tmp1.replace(tmp1[1], "18"))
         
           elif tmp[2] == 1 and tmp[3] == 1:               
              # it is iron, value 19
             newFile.write(tmp1.replace(tmp1[1], "19"))
         
           else:
            # it is air, value 20
             newFile.write(tmp1.replace(tmp1[1], "20")) 

newFile.close()      

如果你能在上面的脚本中解决这个问题,我将不胜感激,那么我想它应该可以工作。

【问题讨论】:

【参考方案1】:

最后,这是您正在寻找的解决方案。

import pandas as pd
df2 = pd.read_csv('Density.topo', header = 0, names = list('ABCD'), delimiter=r'\s+', skiprows=1)
df2[['C', 'D']]= df2[['C', 'D']].round()

new_file_content=''

with open('tryout.hmo', 'r') as f:
    for line in f:

        if line[11:13] == '10':

            if line[3].isspace():
                ID_to_search_for = line[4:8] # number with 4 digits
            else:
                ID_to_search_for = line[3:8] # number with 5 digits
            search_idx = df2[df2['A'] == ID_to_search_for].index[0]

            if df2['C'][search_idx] == 1 and df2['D'][search_idx] == 0:
                change = '18' #magnet
                new_line = line[:11] + change + line[13:]
            elif df2['C'][search_idx] == 1 and df2['D'][search_idx] == 1:
                change = '19' #iron
                new_line = line[:11] + change + line[13:]
            else:
                change = '20' #air
                new_line = line[:11] + change + line[13:]

            new_file_content += new_line

        else:
            new_file_content += line
    
with open('tryout_changed.hmo', 'w') as f:
    f.write(new_file_content)

如果你不想使用数据框,你可以这样做:

with open('density.topo') as f:
    lists_of_list = [line.rstrip().split() for line in f]

new_file_content=''
with open('tryout_test.hmo', 'r') as f:
    
    for line in f:
        
        if line[11:13] == '10':
            if line[3].isspace():
                ID_to_search_for = line[4:8] # number with 4 digits
            else:
                ID_to_search_for = line[3:8] # number with 5 digits
            for idx, sublist in enumerate(lists_of_list):
                if sublist[0] == ID_to_search_for:                    
                    if lists_of_list[idx][2] == 1 and lists_of_list[idx][3] == 0:
                        change = '18' #magnet
                        new_line = line[:11] + change + line[13:]
                    elif lists_of_list[idx][2] == 1 and lists_of_list[idx][3] == 1:
                        change = '19' #iron
                        new_line = line[:11] + change + line[13:]
                    else:
                        change = '20' #air
                        new_line = line[:11] + change + line[13:]

                    new_file_content += new_line

        else:
            new_file_content += line
    
with open('tryout_changed.hmo', 'w') as f:
    f.write(new_file_content)

好的,这是我的最终答案。它(再次)执行您正在搜索的所有内容。如果有问题,请在您的 IDE 中调试您的代码。您应该开始使用上下文管理器,而不是逐步打开和关闭文件。 我在问题中围绕您的代码编写了新代码,并向其中添加了一些 cmets。

oldFileName = 'tryout.hmo'
newFileName = 'tryout_NEW.hmo'
topoFileName = 'Density.topo'

readme = open( oldFileName, "r" )
oldLines = readme.readlines()

m = int(oldLines[3])
print(m)
new_m = m+3
m1 = str(m)
new_m1 = str(new_m)

Phrase = "END_COMP_DATA"
#n = "Phrase not found" #not used --> not needed

with open(oldFileName,"r") as oldFile: 
    for number, lin in enumerate(oldFile):
        if Phrase in lin:
          n = number
       
#insert 3 lines to tryout_new at the right position (--> row n)
magnet = f"      m+1 "'"                     topo_magnet"'"\n" 
iron   = f"      m+2 "'"                       topo_iron"'"\n" 
air    = f"      m+3 "'"                        topo_air"'"\n"  
    
oldLines[n:n] = [magnet, iron, air]

newFile = open(newFileName,"w") 
flag = 0

with open('density.topo') as f:
    data_density = [line.rstrip().split() for line in f]

for idx, row in enumerate(oldLines):
    lst =  row.rstrip() #I think you shouldn't name a variable like a class in python (list). use 'lst' or something like that
    tmp_tryout = lst.split()
    if row.find("BEG_ELEM_DATA") > -1:
        flag = 1
    if flag == 1 and len(tmp_tryout)>1:
        # if the column has more than 2 columns (after split), check for the "10"
        if tmp_tryout[1] == '10':
            # density_idx_line searchs in density.topo for a match with tmp_tryout[0] (e.g. 3749) and stores the whole line
            density_idx_line = list(filter(lambda x: x[0] == tmp_tryout[0], data_density))
            if len(density_idx_line) >0:
                if density_idx_line[0][2] == '1.0' and density_idx_line[0][3] == '1e-05':
                    # the ' 10 ' is the 10 with a whitespace before and after it. Only like this only the 10 gets replaced (and not e.g. 3104 to 3184)    
                    newFile.write(row.replace(' 10 ', ' 18 '))       
                elif density_idx_line[0][2] == '1.0' and density_idx_line[0][3] == '1.0':               

                    newFile.write(row.replace(' 10 ', ' 19 ')) 
                else:

                    newFile.write(row.replace(' 10 ', ' 20 '))
        else: 
            newFile.write(row)
    else: 
        if idx == 3: 
            newFile.write(row.replace(m1, new_m1)) 
        else:
            newFile.write(row)       
                
newFile.close()
print ("script terminated successfully!")

【讨论】:

此代码将有效,但仅适用于此特定文件而不适用于其他文件,很可能我在 python 中搜索解决方案而不使用数据框。我在 python 中尝试了一种解决方案,它几乎可以工作但没有更新值,我会在那里标记你。 其他文件是什么意思?这个文件的格式很乱,所以我只是为这个特殊的文件编写了代码。如果您想更一般地使用它,您可以使用正则表达式来查找所有这些空格之间的不同“列”,这些空格一直在变化。不使用数据框也不应该是一个大问题,只是比较第二个文件中的值的不同方式(因为对我来说更困难的问题是在试用文件中查找和更改值)。是的,如果您让我知道您的最终解决方案是什么样的,我将不胜感激。 是的,我在更改试用文件中的值时也面临同样的问题。我将编辑我的问题并将代码放在我上面的问题中。如果你能找到这方面的灵魂,我将不胜感激,然后它可以用于所有其他文本文件。 我已经在没有问题的答案解决方案部分添加了代码。请在上面检查。此外,在不使用数据框的代码中,仅将值从 10 更改为 20。 我会尽力的。我将在答案中查看并调试您的脚本。您使用什么 IDE,或者您在哪里编写代码?你为什么不使用我的建议?如果处处为 20,则 if/elif 条件永远不会正确。你也可以在你的地方调试我的代码为什么它不工作......对我来说它工作。【参考方案2】:

好的,这是另一个解决方案。对于阅读本文的其他人:这仍然只是一个临时解决方案,但@Sagar 和我都不知道如何做得更好。

import pandas as pd
df = pd.read_csv('tryout.hmo', header = 0, names = list('ABCDEFGHIJKLM'), delimiter=r'\s+', skiprows=[i for i in range(52362)])
df2 = pd.read_csv('Density.topo', header = 0, names = list('ANOP'), delimiter=r'\s+', skiprows=1)
df2 = df2.iloc[:-3, :]

df3 = df.merge(df2, how='outer', on='A')
df3[['O','P']] = df3[['O','P']].fillna(-1).astype(int).replace(-1, np.nan)

df3['B']= df3.apply(lambda x: 18 if x['B']==10 and x['O']==1 and x['P']==0 else (
    19 if x['B']==10 and x['O']==1 and x['P']==1 else (
    20 if x['B']==10 and x['O']==0 and x['P']==0 else x['B'])), axis=1)

df3.to_csv('new_tryout.csv')

它在不到一秒的时间内完成了代码,因此它比 iterrows 或 itertuples 好得多。 新的 csv 文件包括试用文件和密度文件。它们由试用文件的第一列合并在一起(我猜是 ID)

我没有检查所有这些非常大的文件,但从我检查的几个随机点来看,似乎这种方式有效。

【讨论】:

我检查了它,但是如果您检查了诸如 3749 之类的元素或任何在试用文件中具有值 10 的元素没有更改,则该文件也没有正确合并。它很难使用合并文件,因为我希望它在具有更改值的同一个试用文件中。 是的,试用文件根本没有改变。它是关于 new_tryout 文件的,我刚刚检查了 3749。它的值应该是 18。在将其保存为 new_tryout 文件之前,您可以删除最后 3 列,这样您就只有原始的试用列,没什么大不了的 是的,我的意思是在new_tryout文件中,3749行如下:2765,3749,10.0,106.0,10690.0,10692.0,10693.0,10695.0,10658.0,10689.0,,,,,2, 1.0,0.0.,所以 10 的值在我的 csv 中没有改变。此外,正如您所说,new_tryout 文件需要删除最后三列和第一列,并添加试用文件的其余部分,然后它将是更改值的试用文件。我猜这变得更复杂了,你怎么看? 很奇怪。您是否完全按照我发布的方式复制粘贴了我的代码?也许你有不同的python版本。对于您问题的第二部分:删除列很容易。由于列不一致,添加试用文件的第一部分可能有点棘手。我会尽力。不过,这对我来说总是很好的做法。 我使用的是 3.9.5 python 版本。【参考方案3】:

我还是 Python 的初学者,但我试图解决您的问题,这是我的解决方案:

我想有更好的方法可以做到这一点,因为您必须在比较之前将所有数据导入数据框。

另外我不知道您是否可以使用 pd.read_csv 将数据读取到数据框,因为我不知道 *.hmo 和 *.topo

import pandas as pd

df = pd.read_csv('tryout.csv', delimiter=';')
df2 = pd.read_csv('density.csv', delimiter=';')

for idx, row in df.iterrows():
    for idx2, row2 in df2.iterrows():
        if row[0] == row2[0]:
            if row2[2] == 1 and row2[3] == 0 :
                # it is magnet, value 18
                row[1] = 18
            elif row2[2] == 1 and row2[3] == 1 :
                # it is iron, value 19
                row[1] = 19
            else:
                # it is air, value 20
                row[1] = 20
df.to_csv('new_tryout.csv')

我的代码在这里做什么,它将两个文件都加载到数据帧中。然后遍历每一行以比较两个文件中的 ID 相同的位置(例如 3749)。 如果为真,则有 3 个 if 语句是否是磁铁/铁/空气,并将 df 中的值更改为正确的数字。

最后将新的df保存到一个新文件'new_tryout.csv'

我为它创建了 2 个测试文件,它按应有的方式工作。

【讨论】:

感谢您的回复。它在数据框中很容易,但主要问题是试用文件很难转换为 csv,因为它具有不同大小的行和列,这在我上面解释的整个试用文件中有所不同,我不确定元素列是行 [0]这是第一列(如果它在数据框中,但我猜不是在这里)这就是为什么我尝试使用 python 并且仅在试用文件中的 BEG_ELEM_DATA 中需要从 10 更改为 18 或 19 或 20。如果你愿意,我可以分享我的文件,但这里没有分享选项,是吗? 您知道 BEG_ELEM_DATA 是否具有相同的列大小吗?然后您可以跳过 BEG_ELEM_DATA 开始之前的所有行,然后按照建议将其读入 我尝试了使用 pd.read_csv(r'path\file name.txt) 和 to_csv 方法进行转换的常用方法,但问题是这些值不在单独的列中:对于密度文件- 一列中的所有值,如 3749 10 1.0 0.0 和试用文件中的所有值。你知道如何解决这个问题吗? 不知道有没有直接分享数据的方法。您可以将其上传到某处并在此处复制链接。 如果我理解正确,您只需将分隔符设置为制表符 (delimiter='\t')

以上是关于如何遍历 2 个文件中的行,比较值并在满足条件时更新文件中的值?的主要内容,如果未能解决你的问题,请参考以下文章

CSP201809-3 元素选择器

如何比较两个for循环的值并在django模板中使用if语句?

比较 2 个表中的值并生成具有差异的新表

是否可以就地修改文件中的行?

excel满足多个条件后返回固定值并求和怎么用函数实现?

如果在 R 中满足条件,则从前一行获取值并连接