Python: каждый блок данных состоит из переменного количества строк, как определить общее количество блоков?

У меня есть очень большой файл данных, и каждая запись выглядит примерно так:

    5 (this can be any number, call this line n)
Line 1
Line 2
Line 3
n lines, in this case 5, i.e. lines 4 - 8
Line 9
n lines, in this case again 5, i.e. lines 10-14
Line 15

По сути, каждая запись начинается с одной строки, за которой следуют 3 строки + n строк + 1 строка + n строк + 1 строка.

Это число n является целым числом (но может варьироваться в зависимости от записи). Есть ли способ выяснить, сколько записей данных у меня есть в этом файле?

У меня есть некоторый код для того, чтобы, если я знаю, сколько существует записей, тогда я могу циклически проходить по каждой записи... но есть ли способ выяснить количество записей в первую очередь?

Спасибо!

редактировать: вот два примера примера записи -

    5
10.0 0.0 0.0
0.0 10.0 0.0
0.0 0.0 10.0
A       -0.005364798      -0.022912843       0.017346957
B        0.527031905       0.603310150       0.560736787
B       -0.629466850      -0.628385741       0.628048126
B       -0.649090857       0.603667874      -0.726135880
B        0.683741908      -0.584386774      -0.700569743
    -17.862057
  -2.022841336      -1.477407454      -5.606136767
   2.521789668       2.889251770       2.572440406
  -0.401914888      -0.722582908       0.244151982
   0.806040926      -0.990697574       1.474733506
  -0.903074369       0.301436166       1.314862295
      0.016462

     7
 10.0 0.0 0.0
 0.0 10.0 0.0
 0.0 0.0 10.0
 A       -0.591644968      -0.645755982      -0.014245979
 B        1.198655655      -0.588872080      -0.025169784
 B       -1.460774580      -1.255848596       0.025804796
 B        0.321839745       2.199107994       0.050450166
 C        0.617684720      -1.389588077      -0.075897238
 C        0.493712792       1.349385956      -0.004249822
 D       -0.808145644       0.577304796       0.014326943
    -26.435922
   1.649465696      -2.945456091      -0.152209323
   0.531241391      -1.113956273      -0.135548573
  -0.529287352      -0.556746737      -0.061346528
  -2.152476371       6.326868481       0.441458459
  -1.633473432       3.325310912       0.291306019
   0.726490986      -8.268565793      -0.512575180
   1.408090505       3.232545501       0.128915126
      0.155658

Первое число, целое число (5 или 7 в этих примерах), определяет количество строк, следующих за этой записью:

 10.0 0.0 0.0
 0.0 10.0 0.0
 0.0 0.0 10.0

А также количество строк, следующих за строкой после, которая в первом случае: -17.862057

Каждая запись выглядит примерно так. По сути, цель состоит в том, чтобы выяснить, сколько всего записей существует, используя тот факт, что первое целое число дает представление о том, сколько строк следует за оставшейся частью записи.

1 ответ

Решение

Я написал этот код для работы с вашим примером. В начале он не знает, сколько записей, но просто продолжает чтение из файла, пока файл не будет исчерпан, чтобы получить каждую запись. Я сохранил ваш пример ввода в input.txt, Теперь я также изменил код для чтения данных в виде чисел с плавающей запятой.

import pprint
import functools

#helper function for reading multiple lines
def read_n(in_file, n):
    return [in_file.readline() for _ in range(n)]

#read one line of floats
def read_floats(line):
    return list(map(float, line.split()))

#reads several lines of floats
def float_lines(lines):
    return [read_floats(line) for line in lines]

def parse_entry(in_file):
    #get n
    n = in_file.readline().strip()

    if n:
        n = int(n)

        #read 3, n, 1, n, 1 lines
        head = float_lines(read_n(in_file, 3))
        head_data = [(line[0], read_floats(line[1:])) for line in map(str.strip, read_n(in_file, n))]
        mid = float(in_file.readline().strip())
        tail_data = float_lines(read_n(in_file, n))
        tail = float(in_file.readline().strip())

        #readline to eat the empty line between entries
        in_file.readline()

        return n, head, head_data, mid, tail_data, tail

with open("input.txt", "r") as input_file:
    #apply parse_entry until it stops returning
    entries = list(iter(functools.partial(parse_entry, input_file), None))

print(len(entries))
pprint.pprint(entries)

Какие выводы:

2
[(5,
  [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]],
  [('A', [-0.005364798, -0.022912843, 0.017346957]),
   ('B', [0.527031905, 0.60331015, 0.560736787]),
   ('B', [-0.62946685, -0.628385741, 0.628048126]),
   ('B', [-0.649090857, 0.603667874, -0.72613588]),
   ('B', [0.683741908, -0.584386774, -0.700569743])],
  -17.862057,
  [[-2.022841336, -1.477407454, -5.606136767],
   [2.521789668, 2.88925177, 2.572440406],
   [-0.401914888, -0.722582908, 0.244151982],
   [0.806040926, -0.990697574, 1.474733506],
   [-0.903074369, 0.301436166, 1.314862295]],
  0.016462),
 (7,
  [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]],
  [('A', [-0.591644968, -0.645755982, -0.014245979]),
   ('B', [1.198655655, -0.58887208, -0.025169784]),
   ('B', [-1.46077458, -1.255848596, 0.025804796]),
   ('B', [0.321839745, 2.199107994, 0.050450166]),
   ('C', [0.61768472, -1.389588077, -0.075897238]),
   ('C', [0.493712792, 1.349385956, -0.004249822]),
   ('D', [-0.808145644, 0.577304796, 0.014326943])],
  -26.435922,
  [[1.649465696, -2.945456091, -0.152209323],
   [0.531241391, -1.113956273, -0.135548573],
   [-0.529287352, -0.556746737, -0.061346528],
   [-2.152476371, 6.326868481, 0.441458459],
   [-1.633473432, 3.325310912, 0.291306019],
   [0.726490986, -8.268565793, -0.51257518],
   [1.408090505, 3.232545501, 0.128915126]],
  0.155658)]

Продемонстрировав, что он нашел 2 записи, проанализировал их как числа с плавающей запятой и затем вывел записи. Я не совсем уверен, что это за записи, поэтому я назвал их неоднозначно. Обратите внимание, что я сохранил как можно больше данных о записях в моей большой структуре списков-кортежей, потому что я не уверен, какие биты также актуальны, поэтому исходный файл должен быть практически восстанавливаемым из записей в памяти.

Что касается строк, начинающихся с символа - это достигается путем первого применения str.strip в строке, так как иногда перед символом есть пробел. Затем отделяет line в line[0] а также line[1:], который является символом, и фрагментом строки, представляющей данные, который затем обрабатывается как обычно.

Подробнее о том, как я отделяю персонажей от поплавков:

Возьмите следующую строку:

 A       -0.005364798      -0.022912843       0.017346957

Это будет проанализировано:

head_data = [(line[0], read_floats(line[1:])) for line in map(str.strip, read_n(in_file, n))]

Однако, если мы рассматриваем только эту строку, мы можем посмотреть на меньшее из выражения. Первое, что происходит с линией str.strip, от map(str.strip..), Это удаляет любые конечные и начальные пробелы, чтобы гарантировать, что первый символ - это буква, которую нужно удалить. Это означает, что состояние строки в памяти теперь:

"A       -0.005364798      -0.022912843       0.017346957"

Затем линия разделяется на line[0] а также read_floats(line[1:]), Вот где проводится различие между строкой и плавающей точкой - строка отделяется от остальной части строки, которая затем передается read_floats, Это использует нотацию фрагментов, мощный синтаксис, который есть в Python для получения списков итерируемых элементов. Ломтик 1: означает "отрезок от индекса 1 до конца строки". Для ясности:

line[0] == "A"
line[1:] == "      -0.005364798      -0.022912843       0.017346957"

for _ это соглашение Python, когда вам просто нужно что-то повторить, не отслеживая, какое это повторение. то есть он читает строку для каждого числа в range(n)так что он читает n линий, но не нужно отслеживать, какой номер является текущей строкой. С таким же успехом можно сказать for i in range(n), Кроме i будет неиспользованным, поэтому итератор называется _ чтобы указать, что вы не хотите этого.

if n: проверяет, является ли строка n не пусто Это потому что когда ты readline() файл, который был исчерпан, возвращается пустая строка. Это означает, что вместо сбоя, когда это сделано с файлом, программа просто аккуратно прекратит синтаксический анализ записей. Это важно, так как мы не знаем количество записей, поэтому мы продолжаем пытаться прочитать n пока мы больше не можем читать nпоэтому мы должны использовать оператор if.

Относительно того, почему записи выглядят такими запутанными - parse_entry(input_file) будет анализировать только одну запись. Весь остальной багаж необходим для разбора всех записей. functools.partial(parse_entry, input_file) означает "применить аргумент input_file к функции parse_entry". Это затем использует iter продолжать делать это, пока не вернется None, Это довольно полезный трюк - функции iter может быть присвоена любая функция, а затем значение, на котором она будет останавливаться, и она будет возвращать значения из функции до тех пор, пока не достигнет значения "stop". Более простой и часто встречающийся пример может быть iter(sys.stdin.readline, "a\n"), Это будет продолжать читать строки из stdin пока он не попал в строку, содержащую только a,

На кортежах и распаковке кортежей - вы можете сделать это:

for n, head, head_data, mid, tail_data, tail in entries:
    print("n is {}".format(n))
    print("the first item of head_data is {}".format(head_data[0]))

    for i in tail_data:
        print("tail data item: {}".format(i))

Это приводит к выводу:

n is 5
the first item of head_data is ('A', [-0.005364798, -0.022912843, 0.017346957])
tail data item: [-2.022841336, -1.477407454, -5.606136767]
tail data item: [2.521789668, 2.88925177, 2.572440406]
tail data item: [-0.401914888, -0.722582908, 0.244151982]
tail data item: [0.806040926, -0.990697574, 1.474733506]
tail data item: [-0.903074369, 0.301436166, 1.314862295]
n is 7
the first item of head_data is ('A', [-0.591644968, -0.645755982, -0.014245979])
tail data item: [1.649465696, -2.945456091, -0.152209323]
tail data item: [0.531241391, -1.113956273, -0.135548573]
tail data item: [-0.529287352, -0.556746737, -0.061346528]
tail data item: [-2.152476371, 6.326868481, 0.441458459]
tail data item: [-1.633473432, 3.325310912, 0.291306019]
tail data item: [0.726490986, -8.268565793, -0.51257518]
tail data item: [1.408090505, 3.232545501, 0.128915126]

Надеюсь, это демонстрирует, как вы можете использовать структуру.

Другие вопросы по тегам