Как реализовать продолжение строки pythonic читать - PullRequest
0 голосов
/ 30 мая 2019

Я пытаюсь реализовать скрипт Python для чтения и извлечения строк из текстового файла ASCII.Это кажется довольно простой вещью, однако я столкнулся с проблемой, которую не могу решить сам.Файл, который я пытаюсь прочитать, содержит тест, и некоторые строки начинаются с *tr999.В этом шаблоне могут быть как большие, так и маленькие буквы, количество цифр и присутствие * необязательно.Звездочка также может быть до и после.За этим сигнальным ключевым словом следуют цифры, либо int, либо folat.Чтобы поймать сигнал, я использую выражение регулярного выражения python

re.search("[*]{0,1}[Tt][Rr][0-9]{1,5}[*]{0,1}",line)

Текстовый файл выглядит следующим образом

tr10* 1 2 3 22 1 1 13 12 33 33 33
*Tr20 12 22 -1 2  2 2 5 5 5 6 6 6 77
Tr20 1 1 1 &
           2 0 0
           1 1 1
           2 2 2
c that is a comment and below is the problem case '&' is missing
*tr22221 2 2 2
         1 1 1
         2 2 2

Код, который я написал, не может поймать последний случай.Где отсутствует сигнал продолжения линии &.Использование & для продолжения строки не является обязательным и может быть заменено несколькими пробелами в начале продолжения строки.

Код, который я написал:

import sys

fp=open(sys.argv[1],'r')
import re 

# get the integers only
def loop_conv(string):
        conv=[]
        for i in string.split(" "):
            try:
                conv.append(float(i))
            except ValueError:
                pass
        return conv

# extract the information
def extract_trans_card(line,fp):
            extracted=False
            if len(line)>2 and not re.search("[cC]",line.split()[0]) and re.search("[*]{0,1}[Tt][Rr][0-9]{1,5}[*]{0,1}",line) :
                extracted=True
                trans_card=[]
                trans_card.append(line.split()[0])
                line_old=line
   # this part here is because after the read signal,
   # data to be extracted might be on the same line             
                for val in loop_conv(line):
                        trans_card.append(val)
# this part here fails. I am not able to catch the case '&' missing.
# i tried to peek the next line with seek() but it i got a system error. 
# the idea is to loop until i have a continue line case  
                while (re.search("^(\s){5,60}",line) or re.search("[&$]",line_old)) and len(trans_card) <13:

                    line=fp.readline()
                    for val in loop_conv(line):
                        trans_card.append(val)
                    line_old=line


                #print('M',trans_card)
                print('value',trans_card)
                trans_card=[]
            return extracted 



# read the file with a loop
for line in fp:
        if not extract_trans_card(line,fp) :
            print(line,end='')  

вывод:

value ['tr10*', 1.0, 2.0, 3.0, 22.0, 1.0, 1.0, 13.0, 12.0, 33.0, 33.0, 33.0]
value ['*Tr20', 12.0, 22.0, -1.0, 2.0, 2.0, 2.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0, 77.0]
value ['Tr20', 1.0, 1.0, 1.0, 2.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0]
    c that is a comment and below is the problem case '&' is missing
value ['*tr22221', 2.0, 2.0, 2.0]
             1 1 1
             2 2 2

Последняя строка - это проблема.Поскольку 1 1 1 и 2 2 2 игнорируются и просто отражаются.Эта проблема похожа на то, как Python продолжает линию.Либо по пробелам, либо по &.Я надеюсь, что кто-то поможет нам с этим и укажет правильный способ решения этой проблемы

Ответы [ 2 ]

1 голос
/ 31 мая 2019

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

Поскольку начало (заголовок) trans_card можно найти с помощью re.search(r"[*]?[Tt][Rr][0-9]{1,5}[*]?", было бы легче обработать предыдущую trans_card всякий раз, когда эта header шаблон обнаружен.

Ниже приведен пример кода, который я примерно скопировал из вашей логики кода и сохранил получившуюся trans_card в список списков:

import sys
import re

# get the floats only from line, copied from your code
def loop_conv(string):
    conv=[]
    for i in string.split(" "):
      try:
        conv.append(float(i))
      except ValueError:
        pass
    return conv

# set previous trans_card with non-EMPTY vals list
def set_prev_trans_card(card, vals):
    if len(vals):
        card.append(vals)
        #print ('value: {}'.format(vals))

# below new code logic:
with open(sys.argv[1], 'r') as fp:
    trans_card = []

    # a list to save items retrieved from lines associated with the same trans_card
    values = []

    # set up a flag to identify header
    is_header = 0

    for line in fp:
        # if line is a comment, then skip it 
        if re.search("[cC]",line.split()[0]):
            #print(line, end='')
            continue

        # if line is a header, append the existing values[] (from the previous trans_card) 
        # list to trans_card[] and then reset values[]
        if len(line)>2 and re.search(r"[*]?[Tt][Rr][0-9]{1,5}[*]?", line):
            # append values[] to trans_card
            set_prev_trans_card(trans_card, values)

            # reset values[] to the first \S+ on the header 
            values = [ line.split()[0] ]

            # set is_header flag to 1
            is_header = 1

        # if line ends with &\n, then concatenate the next lines
        while line.endswith('&\n'):
            line += ' ' + fp.readline()

        # add all numbers(floats) from header or lines starts with 5-60 white-spaces into the values[] list, and reset is_header flag to 0
        if is_header or re.search("^(\s){5,60}",line):
            values.extend(loop_conv(line))
            is_header = 0

    # append the last values[] to trans_card
    set_prev_trans_card(trans_card, values)

for v in trans_card:
    print ('value: {}'.format(v))

вывод:

value: ['tr10*', 1.0, 2.0, 3.0, 22.0, 1.0, 1.0, 13.0, 12.0, 33.0, 33.0, 33.0]
value: ['*Tr20', 12.0, 22.0, -1.0, 2.0, 2.0, 2.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0, 77.0]
value: ['Tr20', 1.0, 1.0, 1.0, 2.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0]
value: ['*tr22221', 2.0, 2.0, 2.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0]

Примечание: Я пропустил условие len(trans_card) <13 в вашем коде, подумал, что оно просто используется для предотвращения бесконечного цикла while.в противном случае это должно быть легко добавить в приведенный выше пример кода.

Кстати.Возможно, вы захотите добавить ^ к шаблонам комментариев и заголовков, чтобы они соответствовали только началу строк, а не где-либо в строках поиска.

0 голосов
/ 30 мая 2019

Вот Pythonic способ обработки файла (на самом деле все, что можно повторять, когда элемент next () возвращает строку, возможно, заканчивающуюся символом новой строки или нет), где продолжение можно обозначить как ' & 'в последнем столбце текущей "записи" (Python фактически использует' \ ') или пробел в следующей "записи":

import re


def read_lines_with_continue(iter):
    """This function is passed an interator where each iteration returns the next line.
       This function processes logical continuations consisting of lines that end with '&' or lines
       that begin a space."""

    next_line = ''
    saw_continue = True
    for line in iter:
        # get rid of any trailing '&'
        edited_line = re.sub(r'&$', '', line)
        if saw_continue:
            next_line += edited_line
            saw_continue = False
        elif line[0] == ' ':
            next_line += edited_line
        elif next_line != '':
            yield next_line
            next_line = edited_line
        if line != edited_line:
            saw_continue = True
    if next_line != '':
        yield next_line


lines = [
    '1abc',
    '2def&',
    'ghi',
    ' xyz',
    ' ver&',
    'jkl',
    '3aaa',
    '4xxx',
    ' yyy'
]


# instead of passing a list, you could also pass a file
for l in read_lines_with_continue(lines):
    print(l)

1abc
2defghi xyz verjkl
3aaa
4xxx yyy
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...