【问题标题】:How to sort a text file line-by-line如何逐行对文本文件进行排序
【发布时间】:2019-09-30 21:40:36
【问题描述】:

我需要按升序对文本文件进行排序。文本文件的每一行都以一个索引开头,如下所示:

2       0       4         0d 07:00:38.0400009155273
3       0       4         0d 07:00:38.0400009155273
1       0       4         0d 07:00:38.0400009155273   

想法结果如下:

1       0       4         0d 07:00:38.0400009155273
2       0       4         0d 07:00:38.0400009155273
3       0       4         0d 07:00:38.0400009155273 

请注意,这个文本文件有 +300 万行,每个元素自然被认为是一个字符串。

我已经搞砸了一段时间了,没有任何运气,所以我想是时候咨询专家了。谢谢你的时间!

编辑:

我在 Spyder IDE 中使用带有 Python 3.7 的 Windows 操作系统。该文件不是 CSV,它是一个以制表符分隔的文本文件。有可能并非所有索引都存在。原谅菜鸟,我没有很多编码经验。

【问题讨论】:

  • 如果您在任何类 Unix 系统上:sort -n /path/to/file
  • 在linux下运行,应该很快。排序 -o out.txt input.txt
  • 你卡在哪里了?您发现了问题:每个索引自然被认为是一个字符串。因此,您必须将其转换为int,然后进行排序。或者,正如@FMc 指出的那样,只需在您的 shell 中使用 sort 命令。
  • FYI 最好包括您“暂时搞砸这个”的尝试,这样我们就可以知道您具体尝试了什么以及为什么它不适合。否则我们最终可能会发布您已经尝试过的答案并浪费大家的时间。

标签: python python-3.x file sorting


【解决方案1】:
fn = 'filename.txt'
sorted_fn = 'sorted_filename.txt'

with open(fn,'r') as first_file:
    rows = first_file.readlines()
    sorted_rows = sorted(rows, key=lambda x: int(x.split()[0]), reverse=False)
    with open(sorted_fn,'w') as second_file:
        for row in sorted_rows:
            second_file.write(row)

这应该适用于 3+ 百万行的文本文件。使用int(x.split()[0]) 会将每行中的第一项排序为整数

编辑删除 close() 语句

【讨论】:

  • 你不需要代码中的最后两行,因为使用with 会自动关闭文件
  • 谢谢,我不知道。
  • 另一个性能说明,您可以将拆分限制为 1,因此,int(x.split()[0]) 可以是 int(x.split(maxsplit=1)[0]) 以仅拆分“行数和其余部分”,因为您将使用第一个拆分其他拆分将被忽略
  • @Cooper 有没有办法在特定行之后启动 lambda 函数?例如,我想从第 3 行开始,但我对 Lambda 函数不太熟悉。过去我做过类似的事情:for x, y in enumerate(rows): if x > 3: start sort
  • 你可以这样做:sorted_rows = rows[:2]+sorted(rows[2:], key=lambda x: int(x.split()[0])
【解决方案2】:

一站式解决方案是使用一个文件句柄进行读取、排序和写入。感谢'r+'模式:

with open('your_file.txt', 'r+') as f:
    sorted_contents =  ''.join(sorted(f.readlines(), key = lambda x: int(x.split(' ')[0])))
    f.seek(0)
    f.truncate()
    f.write(sorted_contents)

【讨论】:

    【解决方案3】:

    我会通过将文件读入行来解决这个问题,将它们拆分为空格,然后根据自定义键对它们进行排序;即,如果您的文件名为“foo.txt”:

    with open("foo.txt") as file:
        lines = file.readlines()
        sorted(lines, key=lambda line: int(line.split()[0]))
    

    之后,行应包含按第一列排序的所有行。

    但是,关于您的文件大小,我不知道这将如何运作。也许您必须将文件的内容拆分为您一一排序的块,然后您可以对这些块进行排序。

    【讨论】:

      【解决方案4】:

      使用 pandas 会对您有很大帮助。假设文件是​​ csv,请执行以下操作:

      import pandas as pd
      df = pd.read_csv('to/file', sep='\t', index='Name of column with index')  # Guessing that your file is tab separated
      df.sort_index(inplace=True)
      

      现在您有了一个数据框,其中包含您需要排序的所有信息。我建议深入研究熊猫,因为它真的会帮助你。这是一个开始的链接https://pandas.pydata.org/pandas-docs/stable/getting_started/10min.html

      【讨论】:

      • pandas 有相当大的内存开销,这可能会阻止这在大型数据集上成功。由于这个原因,我们无法在类似大小的数据集上使用它(记录数只有几百万)。
      • 在这种情况下,您可以尝试使用 Dask docs.dask.org/en/latest/dataframe.html 当然,当您正在寻找手术刀时,它可能是一把斧头
      【解决方案5】:

      我会使用一个简单的.split(' ') 将数据格式化为如下所示的字典:

      my_data = {
       2: ['0', '4', '0d', '07:00:38.0400009155273'],
       3: ['0', '4', '0d', '07:00:38.0400009155273'],
       1: ['0', '4', '0d', '07:00:38.0400009155273']
      }
      

      然后您可以遍历(假设所有键都存在),例如:

      for i in range(1, max(list(my_data.keys())) + 1):
          pass # do some computation
      

      此外,您还可以选择一个特定的值,例如 my_data[1]

      为了能够以这种形式放置您的数据,我将使用脚本:

      with open("foo.txt", "r") as file:
          in_data = file.readlines()
      
      my_data = {}
      for data in in_data:
          split_info = data.split(" ")
          useful_data = [item.strip() for item in split_info[1:] if item != ""]
          my_data.update({split_info[0]: useful_data})
      
      for key in sorted(my_data.keys()):
          print("{}: {}".format(key, my_data[key]))
      

      哪些打印:

      1: ['0', '4', '0d', '07:00:38.0400009155273']

      2: ['0', '4', '0d', '07:00:38.0400009155273']

      3: ['0', '4', '0d', '07:00:38.0400009155273']

      【讨论】:

        【解决方案6】:

        这是您已有的完美答案的编辑版本。编辑 当您了解有关编码的更多信息时可能会很有用。重点:

        • 编写程序时,通常最好使用小样本进行编码 输入数据(例如,一个有 30 行而不是 300 万行的文件): 您的程序将运行得更快;调试输出会越来越小 可读;以及其他一些原因。因此,而不是硬编码 输入文件(或其他文件)的路径,将这些文件路径作为命令行 参数,使用sys.argv

          import sys
          
          in_path = sys.argv[1]
          out_path = sys.argv[2]
          
        • 如果你在内存中保存了大量数据(足以让你认为你是 接近您机器的限制),不要创建不需要的数据副本。为了 例如,要忽略前几行,不要将原始行存储在 rows 然后使用 rows[2:] 获得所需的值:创建一个新的 列表。而是将条件逻辑添加到您最初创建的 rows( 示例使用列表推导,但您可以在常规中执行相同的操作 for 循环)。如果您需要对这些数据进行排序,请不要使用sorted(),它 创建一个新列表;而是使用 rows.sort() 对列表进行适当的排序。

          with open(in_path, 'r') as fh:
              rows = [line for i, line in enumerate(fh) if i > 1]
              rows.sort(key = lambda x: int(x.split(None, 1)[0]))
          
        • 没有理由将写入 with-block 嵌套在读取中 带块。如果您没有充分的理由连接两个不同的任务 在一个程序中,明确地将它们分开。这是最重要的 编写更好软件的关键。

          with open(out_path, 'w') as fh:
              for r in rows:
                  fh.write(r)
          

        【讨论】:

        • 能否详细说明上面的硬编码路径v.命令行参数语句?我一直在调试的方式是在一个列表中包含 3 个示例文件,即 sample_f = ['file_1.txt', 'file_2.txt']。然后我创建一个函数并将 sample_f 列表用作输入参数。我不完全理解您使用 in_path = sys.argv[1] 和 out_path = sys.argv[2] 的理由。
        • 感谢您抽出宝贵时间解释上述帖子。非常感谢。
        • @Tim51 我假设您正在从命令行运行您的程序(例如,从 Bash shell、Windows 终端或类似的终端):例如,python your_script.py。如果这是正确的,我的建议是接受输入和输出路径作为用户提供的参数:例如,python your_script.py small_file.txt。换句话说,参数化你的脚本,而不是硬编码文件路径。一般的观点是:出于测试/调试目的,您希望能够以较小的输入和输出执行您的脚本无需编辑您的源代码
        • 我明白了。我正在 Windows 10 上从 Spyder IDE 运行我的程序。但感谢您的提示。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多