Escrevendo um programa para editar dados .txt - Python ou Unix?

2

Eu só tenho pouca experiência em programação e atualmente estou trabalhando para melhorar minhas habilidades. Basicamente, eu preciso escrever um programa, que pode fazer alguns processos específicos para alguns dados em um arquivo .txt.

Para começar do zero, tenho um arquivo .txt com dados assim:

>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
//     x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
//         x  x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
//     x   x

Eu preciso fazer algumas coisas estranhas para esses dados, a fim de acabar com um conjunto de dados, que pode ser analisado no software que eu uso. Cada "//..."-line refere-se ao grupo de dados acima, até a próxima" //..."- line

Aqui está uma lista do que eu quero fazer:

Desloque o "//..."-line, para que o grupo de dados a que ele se refere esteja abaixo dessa linha, e não acima dele:

//         x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
//         x  x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
//              x   x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv

Adicione um nome exclusivo a cada grupo após //, sem alterar o texto restante na linha:

//Name 1  x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
//Name 2   x  x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
//Name 3        x   x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv

Envie isto para um novo arquivo, sem alterar o original. Em seguida, pegue cada linha de nome + linha abaixo e imprima isso em um arquivo2:

//Name 1  x
>tex_1 abcdefghijklmnopqrstu
//Name 2   x  x
>tex_1 abcdefghijklmnopqrstu
//Name 3        x   x
>tex_1 abcdefghijklmnopqrstuv

Altere a estrutura, de modo que a nomenclatura seja semelhante à seguinte e exiba isso para o File3:

>Name 1 abcdefghijklmnopqrstu
>Name 2 abcdefghijklmnopqrstu
>Name 3 abcdefghijklmnopqrstuv

Os dados acima estão em uma estrutura, eu posso realmente analisar.

Agora eu sei que isso é uma tarefa fácil, e não estou perguntando "como eu programo isso? Gostaria apenas de saber onde vocês começariam com esse projeto, e que linguagem você acha que combina com projeto melhor?

Eu consegui fazer algumas coisas no unix, obtendo ajuda neste site. Por exemplo. Dar nomes exclusivos a cada linha "// ...", pelo seguinte código unix:

awk -F '' '/\/\//{n++ ; t=" Name "n ; sub("// {0,"length(t)-1"}","//"t)}{print}' File1.txt

Você poderia me dar algumas dicas de onde começar?
O problema é adequado como um projeto Python?
O arquivo de dados .txt original contém muitos dados, portanto, não é possível fazer o processamento manualmente. Este projeto também serve como uma maneira de avançar na programação.

    
por Hjalte 29.01.2015 / 17:12

3 respostas

3

Isso parece mais um trabalho python para mim. Uma regra geral é: se sua tarefa requer apenas um processamento "plano" e cego para o conteúdo, os utilitários principais (preferencialmente o gnu) são o caminho a percorrer. Isso vale para a substituição de strings, deleção, processamento baseado em linha, classificação simples, contagem, filtragem e assim por diante ... essas ferramentas permitem que você escreva rapidamente um one-liner para fazer o que quiser, com a prática nem requer muito pensamento.

Por outro lado, se você precisa de um trabalho complexo que precisa examinar o arquivo, implica em uma estrutura hierárquica (tipo árvore), delimitadores personalizados e sequências com reconhecimento de contexto, é muito mais fácil ter a mesma estrutura disponível na sua linguagem de programação. Um exemplo extremo são os arquivos html / xml / json, quaisquer arquivos com chaves aninhadas e assim por diante ... se você tem estruturas de dados muito poderosas disponíveis (o python faz), simplesmente grave-as, processe-as de maneira semelhante a objetos e leia-as voltar. Você ainda pode fazê-lo no awk (ou com um pouco mais de esforço no sed), mas não vale a pena.

No seu caso, você precisará memorizar a posição do marcador anterior (ou manter um buffer longo), que fica um pouco desajeitado com o software de processamento de linha. Em python, no entanto, é fácil.

Exemplo de código:

#!/usr/bin/python
import sys

with open(sys.argv[1],'r') as file, open(sys.argv[2],'w') as file1, open(sys.argv[3],'w') as file2, open(sys.argv[4],'w') as file3:
    counter = 1
    current_buffer = []
    for line in file:
        if line.startswith('//'):
            #we found a delimiter, flush the buffer
            #could use regular expressions, but for the sake of this example
            #this is enough
            prefix = '//Name {}'.format(counter)
            new_header = prefix+line[len(prefix):]
            file1.write(new_header)
            file2.write(new_header)
            for oldline in current_buffer:
                file1.write(oldline)
            if current_buffer: #only first line to file 2
                first_line = current_buffer[0]
                file2.write(first_line)
                #same here, could use regular expressions from "import re" but we don't have to
                rest_of_line = first_line.split(' ',1)[1]
                file3.write('//Name {} {}\n'.format(counter,rest_of_line.strip()))
            current_buffer=[]
            counter+=1
        else:
            current_buffer.append(line)

    #if the file is well-formatted, current_buffer should be empty now - otherwise, do special handling

Chame como ./test.py inputfile file1 file2 file3 e veja se é o que você queria.

    
por 29.01.2015 / 17:36
3

Você deve ir com o que for mais confortável para você. Dito isso, você não deve evitar aprender o uso de uma nova ferramenta também. Estou muito confortável com sed , e acredito que nl é adequado exclusivamente para essa tarefa e, portanto, usando uma combinação dessas duas ferramentas que fiz:

<<\INFILE \
nl -bp'^//' -w1 -s'      ' |\
sed -ne ' s|^ *>|>|;//H;x;//x;t' -e 'G
        x;s|.*|### The following is written to: File1|p
        x;s|\([0-9]*\)\( *\)\( \)*//|//Name |pw File1
        x;s|1|2|p;x;s|\n\(\n.*\)*|&&|2;s|||2p;w File2' -e 'x;s|2|3|p
        x;s|//\([^ ]* [^ ]*\).*\n[^ ]*|>|pw File3'
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
//     x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
//         x  x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
//     x   x

... o que parece fazer o truque.

Quando eu digo que nl é exclusivamente adequado para esta tarefa é porque, além de numerar facilmente os blocos em ordem de acordo com um padrão e injetar uma cadeia --separator arbitrária entre o texto na numeração line e o número da linha, nl também sempre recorta as linhas não são numeradas para corresponder ao recuo que elas adicionam àquelas que ele faz.

Isso significa que qualquer linha que não seja numerada como uma correspondência para o padrão ^// agora começa com <space> . E mesmo se o seu texto atual for muito mais complicado que os dados em seus exemplos, o sed é realmente muito fácil de fazer. Na verdade, grande parte do trabalho acima destina-se apenas a gerar uma saída de depuração significativa, pois sed funciona. Uma declaração totalmente funcional poderia ser tão simples quanto:

nl -bp'^//' -w1 -s'      ' <infile |
sed -ne 's|^ *>|>|;//H;x;//x;t' -e 'G;h
         s|\([0-9]*\)\( *\)\( \)*//|//Name |w File1
         s|\n|&&|2;s|\n\n.*||;w File2
         s|//\([^ ]* [^ ]*\).*\n[^ ]*|>|w File3'

Estou um pouco confuso sobre a sua declaração sem mudar o texto restante na linha porque parece contradizer seus exemplos (e o reino da possibilidade) . Eu digo isso porque:

//     x

... é obviamente deslocado um pouco para a direita quando é transformado em:

//Name 1  x

... e de fato o 1 e o x ocupam a mesma coluna nos dois exemplos. Eu tentei encontrar essa contradição no meio - ela não muda o texto, a menos que seja necessário evitar a sobrescrita de um caractere que não seja <space> enquanto ainda insere o par Name [num] mais um espaço único.

Assim, não leva em conta a discrepância entre ...

//Name 3        x   x

... e ...

//     x   x

... que eu desconsiderei como (espero?) um erro no exemplo.

Meu pipeline imprime tudo para stdout, mas os blocos seguindo as linhas que começam com ### são anexados a cada um dos arquivos indicados também. Aqui está o que meu pipeline imprime:

### The following is written to: File1
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrstuv
>tex_4 abcdefghijklmnopqrst
### The following is written to: File2
//Name 1 x
>tex_1 abcdefghijklmnopqrstu
### The following is written to: File3
>Name 1 abcdefghijklmnopqrstu
### The following is written to: File1
//Name 2   x  x
>tex_1 abcdefghijklmnopqrstu
>tex_2 abcdefghijklmnopqrstuv
>tex_3 abcdefghijklmnopqrst
>tex_4 abcdefghijklmnopqrstuv
### The following is written to: File2
//Name 2   x  x
>tex_1 abcdefghijklmnopqrstu
### The following is written to: File3
>Name 2 abcdefghijklmnopqrstu
### The following is written to: File1
//Name 3 x   x
>tex_1 abcdefghijklmnopqrstuv
>tex_2 abcdefghijklmnopqrstuv
### The following is written to: File2
//Name 3 x   x
>tex_1 abcdefghijklmnopqrstuv
### The following is written to: File3
>Name 3 abcdefghijklmnopqrstuv
    
por 30.01.2015 / 16:30
2

Não posso concordar que o Python seja mais aceitável no caso. Não há manipulação de linha difícil - basta definir variáveis e imprimir vários conjuntos. Então, são operações normais de bash. Então, precisamos de alguns cálculos que eu prefiro awk em vez de sed (programa de manipulação de texto usual).

awk -F '' '
          /\/\//{
                 n++
                 t="Name "n" "
                 sub("// {0,"length(t)"}","//"t)
                 print $0 "\n" l1 l2 > "File2"
                 print $0 "\n" l1 > "File3"
                 sub("[^ ]* ","<"t,l1)
                 print l1 > "data.file" 
                 l1=""
                 l2=""
                 next
                 }
          {
                 if (l1=="")
                     l1=$0
                 else l2=l2 "\n" $0
                 }' Input.txt

Mas todos preferem essa ferramenta que ele conhece melhor.

P.S. Quanto ao comprimento da string de entrada. Se OP verificar seus exemplos, você pode ver que «x» is above «e»

//         x
>tex_1 abcdefghijklmnopqrstu

mas depois da entrada Name 1 «x» is above «d»

//Name 1  x
>tex_1 abcdefghijklmnopqrstu

Portanto, no script, não seja length(t) não lenght(t)-1 . Além disso, se não houver espaço suficiente para inserir o script de nome, torne necessário o resíduo da string shift para a direita.

    
por 29.01.2015 / 18:28