【问题标题】:How do I iterate through two arrays individually如何分别遍历两个数组
【发布时间】:2015-06-29 22:44:00
【问题描述】:

我自己的小项目是根据时间戳合并两个日志,两个日志的时间戳是一样的。有些行没有时间戳,应该与带时间戳的行一起打印。

所以如果我有这样的日志:

2015-06-25 09:20:25,654 file1 text2
2015-06-25 09:20:23,654 file1 text1
test text1 belongs to the row above
2015-06-25 09:20:27,654 file1 text3

另一个文件的方式相同,但时间戳不同。 由于我是 Ruby 新手,我发现这个项目可能是一个很好的开始方式。

到目前为止,我已经找到了足够的帮助,我应该使用枚举器,我猜一个

loop do
  code
end

但是我如何决定何时迭代 file1 而没有 file2 是否也会迭代呢? 以及如何找出一个迭代器何时位于文件末尾,以便打印另一个文件的其余部分?

我应该先将文件读入每个数组,还是只对每个文件使用两个流,对输出文件使用一个流?

总结:我想遍历两个文件,直到一个文件到达末尾,然后打印另一个文件中的最后一行,并控制两个文件中应该何时进行迭代。

感谢您的宝贵时间和意见!

**编辑:**

但我想将它们与时间戳合并在一起。喜欢:
2015-06-25 09:20:24,123 file1 text1

2015-06-25 09:20:23,123 file2 text1
2015-06-25 09:20:26,123 file2 text2

输出:
2015-06-25 09:20:23,123 file2 text1
2015-06-25 09:20:24,123 file1 text1
2015-06-25 09:20:26,123 file2 text2

基本上,如果我有两个数组,我会使用迭代器 x 和 y 进行迭代。 如果 x > y 则将 y 放入输出文件并像 y++ 一样继续检查它们,直到文件结束。如果 x 是 eof,只需将 y 的其余部分添加到输出文件中。

【问题讨论】:

  • 您可以使用eof? 检查您是否位于输入文件流的末尾。至于何时要读取文件 1 或 2,这取决于您的要求。当您说“然后打印另一个文件中的最后一行”时,我很难理解您的意思。如果您能澄清我可以在答案中提供示例代码。
  • 所以日志使用相同的语法。 date time info text 一个正常的 Log4j 输出,有时在行的开头有一个堆栈跟踪,并且没有日期时间。因此,当我遍历两个文件并将它们合并到一个大的单个输出文件时,我想获取留在 file2 中的所有数据,如果 file1 到达它的目标或 file2 就将它们全部推送到输出文件,因为我知道时间戳更高,因为它是 file1 中的 eof。我希望这能澄清一些事情。
  • 文件可能有多大?这是我看到的最大问题,因为它们都需要适应内存。
  • 它们可以达到 GB 或 1.5GB。但大多在 800~ish 左右。我正在尝试获得线性解决方案,而不是指数解决方案。但我现在的问题是 Ruby 中的语法以及如何迭代它。我知道 zip,然后可能是某种类型的,但在大日志中可能需要很长时间?在这里使用可能是错误的语言,但我真的很喜欢 Ruby 语法,所以我想学习。
  • 我会继续思考这个问题,看看我的想法。使用 zip 之类的东西需要两个列表同时在内存中,这可能需要避免,因为两个列表之间可能有 3GB。

标签: arrays ruby iterator zip enumerator


【解决方案1】:

好的,这就是我最终想出的。我相信它应该对你有用,具有以下实现:

  • 如果每个文件的当前行以可解析的日期开头,则最早的获胜(将第 49 行的 < 更改为 > 以交换它)。
  • 将一行写入输出时,循环读取该行的文件并抓取下一行。另一个文件中的行保持不变,直到轮到该行被写入为止。
  • 如果一行不是以可解析日期开头,则该行获胜并被写入。如上所述,该文件被循环并拉入下一行,重复直到再次出现可解析的日期或到达文件末尾。
  • 如果其中一个文件到达其末尾,则另一个文件将被流式传输到输出,直到它也到达其输出为止。

请注意,您需要将'file1''file2''output' 更改为相对或绝对的文件路径。您可以使用ARGVOptionParser 使用命令行参数将此数据传递到程序中。

输入文件:

# file1

2014-06-21 07:20:25,654 file1 text2
2015-01-13 14:24:23,654 file1 text1
test text1 belongs to the row above
2015-06-21 08:57:27,654 file1 text3

# file2

2013-01-05 19:27:25,654 file1 text2
2015-04-01 10:13:23,654 file1 text1
test text5 belongs to the row above
2015-06-23 09:49:27,654 file1 text3

# output

2013-01-05 19:27:25,654 file1 text2
2014-06-21 07:20:25,654 file1 text2
2015-01-13 14:24:23,654 file1 text1
test text1 belongs to the row above
2015-04-01 10:13:23,654 file1 text1
test text5 belongs to the row above
2015-06-21 08:57:27,654 file1 text3
2015-06-23 09:49:27,654 file1 text3

# compile_files.rb

require 'date'

# Attempt to read a line from the supplied file.
# If this fails, we are at the end of the file and return nil.
def read_line_from_file(file)
  file.readline
rescue EOFError
  nil
end

# Parse the date which is at the beginning of the supplied text.
# If this fails, it doesn't start with a date so we return nil.
def parse_date(text)
  DateTime.parse(text)
rescue ArgumentError
  nil
end

begin
  # Open the files to sort
  input_file_1 = File.open('file1', 'r')
  input_file_2 = File.open('file2', 'r')

  # Open the file that will be written. Here it is named "output"
  File.open('output', 'w+') do |of|
    # Read the first line from each file
    left = read_line_from_file(input_file_1)
    right = read_line_from_file(input_file_2)

    # Loop until BOTH files have reached their end
    until left.nil? && right.nil?
      # If the first file was successfully read, 
      # attempt to parse the date at the beginning of the line
      left_date = parse_date(left) if left
      # If the second file was successfully read, 
      # attempt to parse the date at the beginning of the line
      right_date = parse_date(right) if right

      # If the first file was successfully read, 
      # but the date was not successfully parsed,
      # the line is a stack trace and needs to be printed
      # because it will be following the related
      # timestamped line.
      if left && left_date.nil?
        of << left

        # Now that we have printed that line, 
        # grab the next one from the same file.
        left = read_line_from_file(input_file_1)
        next

      # If the first file was successfully read, 
      # but the date was not successfully parsed,
      # the line is a stack trace and needs to be printed
      # because it will be following the related
      # timestamped line.
      elsif right && right_date.nil?
        of << right

        # Now that we have printed that line, 
        # grab the next one from the same file.
        right = read_line_from_file(input_file_2)

        # Skip straight to the next iteration of the `until` loop.
        next
      end

      if left.nil?
        of << right

        # Now that we have printed that line, 
        # grab the next one from the same file.
        right = read_line_from_file(input_file_2)

        # Skip straight to the next iteration of the `until` loop.
        next
      end

      # If we got this far, neither of the lines were stack trace
      # lines. If the second file has reached its end, we need
      # to print the line we grabbed from the first file.
      if right.nil?
        of << left

        # Now that we have printed that line, 
        # grab the next one from the same file.
        left = read_line_from_file(input_file_1)

        # Skip straight to the next iteration of the `until` loop.
        next
      end

      # ADDED THIS SECTION
      # If we got this far, the second file has not
      # reached its end. If the first file has reached
      # its end, we need to print the line we grabbed 
      # from the second file.
      if left.nil?
        of << right

        # Now that we have printed that line, 
        # grab the next one from the same file.
        right = read_line_from_file(input_file_2)

        # Skip straight to the next iteration of the `until` loop.
        next
      end

      # If we got this far, neither file has reached its 
      # end and both start with timestamps. If the first file's
      # timestamp is less, it is older.
      if left_date < right_date
        of << left

        # Now that we have printed that line, 
        # grab the next one from the same file.
        left = read_line_from_file(input_file_1)

        # Skip straight to the next iteration of the `until` loop.
        next
      # Either the timestamps were the same or the second one is
      # older.
      else
        of << right

        # Now that we have printed that line, 
        # grab the next one from the same file.
        right = read_line_from_file(input_file_2)

        # Skip straight to the next iteration of the `until` loop.
        next
      end
    end
  end
ensure
  # Make sure that the file descriptors are close.
  input_file_1.close
  input_file_2.close
end

【讨论】:

  • 抱歉回复晚了,有一些事情要处理。所以我的输出只显示为输出。根本没有排序。当你说:if left && left_date.nil?我喜欢“直到 left.nil?&& right.nil?”的想法。因为它会强制两个文件都到最后。但是解析日期呢?你在那里做什么?我正在这样做。 if left[my_regex] 似乎比解析日期更容易。但就像现在一样,我没有把它分类。但我会尝试更改您代码中的一些内容。非常感谢!
  • 我浏览并评论了所有内容,以便您更好地了解我的每一段代码在做什么。我也意识到有一个错误可能导致线条不正确。您可以看到我添加的带有# ADDED THIS SECTION 注释的部分。
  • 我使用了您的代码并更改了一些以符合我的喜好,非常感谢! cmets很棒!
猜你喜欢
  • 1970-01-01
  • 2019-03-20
  • 2010-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 2017-04-08
  • 1970-01-01
相关资源
最近更新 更多