Oi Aureliano, cara, antes de tudo, parabéns. O exemplo que vc colou no email mostra que vc está prestando atenção no que é dito aqui na lista. Seus códigos estão agora com strict (faltou só o 'warnings', hein?), usando autodie, open com 3 argumentos, muito bom mesmo! Agora, pra ficar perfeito, falta só uma coisa: por favor, antes de mandar dúvidas, lembre que as pessoas daqui da lista não estão sentadas aí do seu lado olhando o seu problema, nem vivendo o seu dia-a-dia para saber os motivos e objetivos do código, tampouco possuem (eu pelo menos não possuo) poder de ler mente :-)
Por exemplo, quando vc pergunta algo do tipo: > Monges, cade o erro??? Fica faltando o clássico grupo: "o que vc está tentando fazer, de modo geral? No código específico, o que deveria estar acontecendo mas não está? Ainda no código específico, o que está de fato acontecendo em vez do que deveria?" Quando, mesmo depois do Junior Moraes matar a charada, vc continua com: > Mas me diz uma coisa, como editar um arquivo. A gente tem que se esforçar até pra saber que é uma pergunta, mais ainda qual é a real pergunta por trás da pergunta. "Como editar um arquivo" é quase tão vago quanto "Preciso saber o que estou errando aqui" (retirado de outra thread que vc começou). Percebe o padrão? Isso não é uma bronca. Mas, pensa comigo: se der a impressão que vc não está se esforçando nem pra fazer a pergunta, pq alguém se esforçaria pra te dar a resposta? A atividade na lista é voluntária e não remunerada, feita por pessoas com um interesse em comum (Perl) e que se dispõem a ajudar umas às outras em seu tempo livre. Por isso mesmo, se vc quer respostas melhores e mais rápidas, precisa nos ajudar a te ajudar! Por exemplo, experimente ler suas perguntas em voz alta antes de enviar o email, e veja se elas deixam realmente claro o que você está perguntando. Existe um texto muito bom sobre isso chamado "Como fazer perguntas inteligentes" mas também é grande e tem muitas coisas lugar-comum que você certamente já sabe. Se me permite a sugestão, vá direto nessas aqui, que são onde (eu pelo menos) sinto mais dificuldade: http://www.istf.com.br/perguntas/#writewell http://www.istf.com.br/perguntas/#beprecise http://www.istf.com.br/perguntas/#symptoms http://www.istf.com.br/perguntas/#goal http://www.istf.com.br/perguntas/#examples Não estou falando isso pra vc ficar chateado, pelo contrário! Estou só tentando te ajudar a nos ajudar, e assim tirar maior proveito das listas e melhorar cada vez mais. Os parágrafos acima são uma leitura super rápida, pense como um investimento: vc vai gastar menos de 5 minutos pra ler (e está traduzido em português!) e se prestar atenção nesses pontos, tenho certeza que daqui pra frente você vai conseguir respostas muito mais rapidamente nessa e em qualquer outra lista! Dito isso, deixa eu tentar te ajudar. Eu *acho*, depois de perder um bom tempo olhando pro seu exemplo, que a sua pergunta é: "Pessoal, como eu faço pra abrir o mesmo arquivo tanto para leitura quanto para escrita?" ou "Pessoal, como eu faço para ler e escrever no mesmo arquivo, ao mesmo tempo?" Se for isso mesmo, é uma pergunta super comum. E, como a maioria das coisas em Perl, há mais de uma maneira de se fazer :) Digamos, para efeito de exemplo, que você queira passar o nome de um arquivo como parâmetro para o seu programa. O arquivo tem o formato: ----------8<----------- >blablabla ACTGAACAGTAGCTACTGACTCGTACGCTCGTAGC >lalalala CAGCTGATCGATCGTAGCATGCTACG ---------->8----------- E o que você quer é transformar esse arquivo em algo como: ----------8<----------- >contig0 ACTGAACAGTAGCTACTGACTCGTACGCTCGTAGC >contig1 CAGCTGATCGATCGTAGCATGCTACG ---------->8----------- Claro, não só duas linhas e sim centenas, mas acho que deu pra entender. Solução 1 (one-liner) ================ perl -i -pe 'BEGIN { $i = 0 } $i++ if s{^>.+$}{>contig$i}' arquivo.txt Essa solução usa perl como ferramenta para transformação de arquivos "in place". O "-pe" executa o código entre aspas para cada linha do arquivo passado, imprimindo o que estiver em $_ no final do processamento de cada linha. O código em si é simples: substitua "^>.+$" (início de linha seguido de ">" seguido de qualquer coisa, seguido do fim da linha) pela string ">contig$i", onde $i é um número que começa com zero (por isso o BEGIN { $i = 0 }, senão $i começa vazio, e a primeira linha vira apenas ">contig" em vez de ">contig0"). Sempre que conseguir fazer essa substituição (ou seja, sempre que a linha em questão casar com a regex do lado esquerdo do s{}{}), incremente o valor de $i. Assim, quando a linha casar com a regex, a substituição sera feita, o $i será incrementado, e a linha será impressa (por causa do "-p"). Se a linha não casar com a regex, nada será feito e a linha será impressa sem mudanças (de novo, por causa do "-p"). Executando apenas com o "-pe", ele vai imprimir na tela o resultado pra vc. Isso significa que vc pode redirecionar pra outros arquivos, se quiser. Mas como em nosso problema queremos que o próprio arquivo seja modificado, usamos a opção "-i" (de "in place"), que escreve a saída no próprio arquivo de entrada pra você, sem que você precise ficar se preocupando com criação e cópia de arquivos temporários. Se quiser manter o original para fins de backup, basta trocar "-i" por "-i.bak", e o perl manterá uma cópia do original em "arquivo.txt.bak". Mais detalhes: perldoc perlrun Solução 2 (one-liner, em arquivo) ========================== ----------8<----------- #!/usr/bin/env perl -pi BEGIN { $i = 0 } $i++ if s{^>.+$}{>contig$i} ---------->8----------- One-liners são bacanas, mas são também difíceis de manter e de lembrar. Uma alternativa é copiá-los para arquivos que podem ser executados normalmente. Sabe o "shebang", aquela linha com "#!" que chama o "perl"? Você pode passar parâmetros por ela também. No caso, estamos passando "-p" e "-i" juntos ("-pi"), já que não precisamos do "-e" pois o código está no próprio arquivo. Repare também que o "-i" tem que ser o último parâmetro (sem nada depois), pois se escrevessemos "-ip" o perl vai achar que vc quer editar o arquivo e gravar um backup com extensão "p". Claro que um "one-liner em arquivo" é difícil de manter, porque vc tem que prestar atenção nos modificadores passados (em nosso caso, "-pi") e fazer a "tradução" de cabeça do que de fato está acontecendo. Também tem que, idealmente, adicionar strict, warnings, declarar variáveis, aquilo tudo que é imprescindível para um programa profissional e fácil de manter. Para contornar esse problema, você pode procurar soluções como o App::Rad para converter seus one-liners em programas, ou simplesmente continuar lendo :) Solução 3 (alteração in-place, versão extendida) ===================================== ----------8<----------- #!/usr/bin/env perl use strict; use warnings; # para mudar arquivos 'in-place' # vindos do ARGV. local $^I = q{}; my $i = 0; while ( my $linha = <ARGV> ) { if ($linha =~ s/^>.+$/>contig$i/ ) { $i++; } } continue { die "erro editando arquivo in-place: $!\n" unless print $linha; } ---------->8----------- Essa versão é a forma "extendida" do one-liner. Acho legal ver como uma simples linha se transforma dessa forma, dá outra perspectiva para quanto o perl facilita a nossa vida (outra comparação que sempre me impressiona é Moose x OO tradicional, mas isso é outro papo). A primeira coisa a notar é que estamos inicializando a variável local $^I, uma variável especial do Perl que transforma os arquivos de entrada nos próprios arquivos de saída, exatamente como o "-i" faz na linha de comando. Colocamos a string vazia pois, nesse exemplo, não estamos gravando backup do original. Depois, lemos o conteúdo dos arquivos linha-a-linha e fazemos nossa já conhecida substituição e incremento do $i (dessa vez botei o if na forma "ativa", pra mostrar que tanto faz: vale o que ficar mais claro para você). Finalmente, termos o bloco "continue", que é executado no final de cada linha. Poderíamos ter escrito nosso while sem o continue, assim: ----------8<----------- while ( my $linha = <ARGV> ) { if ($linha =~ s/^>.+$/>contig$i/ ) { $i++; } die "erro editando arquivo in-place: $!\n" unless print $linha; } ---------->8----------- Mas, se o efeito é o mesmo, pq usar continue? Simples: se amanhã vc quiser incrementar seu parser e pular linhas, sair do loop, etc, o que estiver dentro do bloco continue será executado mesmo após chamar "next". O efeito é o mesmo, e vc pode deixar sem o "continue" se te confundir (apenas lembre que ele existe, caso precise desse recurso no futuro). Para mais informações: perldoc -f continue Solução 4 (usando arquivos temporários) ================================ ----------8<----------- #!/usr/bin/env perl use strict; use warnings; use autodie; my $nome_original = 'arquivo.txt'; my $nome_tmp = 'tmp.txt'; open my $orig_fh, '<', $nome_original; open my $tmp_fh, '>', $nome_tmp; my $i = 0; while ( my $linha = <$orig_fh> ) { if ($linha =~ s/^>.+$/>contig$i/ ) { $i++; } } continue { print $tmp_fh $linha; } close $orig_fh; close $tmp_fh; rename $nome_tmp => $nome_original; ---------->8----------- Essa abordagem com arquivo temporário usa menos memória do que a solução sem arquivos temporários mais abaixo. Também é mais segura de trabalhar e fácil de entender, e te dá a oportunidade de gravar um arquivo de backup - é só adicionar um rename($nome_original => "outro_nome") antes do rename que está na última linha do exemplo. Outra vantagem é que vc pode gravar o nome dos arquivos de origem direto no programa ou em um arquivo de configurações, em vez de ser obrigado a passar o nome do arquivo como parâmetro, como nas soluções anteriores. Notas: certifique-se que os arquivos não estão sendo editados por mais ninguém durante o processo, de preferência com lock files. Outra: o rename() não funciona entre sistemas de arquivos, então crie o arquivo temporário de preferência no mesmo diretório do outro ou use File::Copy para uma solução realmente portátil. Solução 5 (sem arquivos temporários) ============================= ----------8<----------- #!/usr/bin/env perl use strict; use warnings; use autodie; open my $fh, '+<', 'arquivo.txt'; my @novas_linhas = (); my $i = 0; while ( my $linha = <$fh> ) { if ($linha =~ s/^>.+$/>contig$i/ ) { $i++; } } continue { push @novas_linhas => $linha; } # volta ao inicio do arquivo, # sobrescreve tudo com as novas # e arranca fora (trunca) o que sobrar seek $fh, 0, 0; print $fh @novas_linhas; truncate $fh, tell($fh); close $fh; ---------->8----------- Essa solução abre o arquivo em modo '+<' (update), o que te permite ler e escrever ao mesmo tempo no arquivo. Note que, se vc le e escreve AO MESMO TEMPO, o "stream de bytes" do arquivo muda o tempo todo, e você certamente vai se perder em relação ao ponto exato no arquivo em que seu handle está. Por isso, a solução é primeiro ler todo o arquivo em memória (ou ler somente as linhas que interessam, já com o conteúdo modificado, como fazemos acima), e só depois que processar todas as linhas, voltar ao início do arquivo e sobrescrever o conteúdo. De novo, faça lock do arquivo ou tenha certeza que ninguém vai editar o arquivo junto com seu programa. Vale ressaltar que essa solução sem arquivo temporário só é recomendada se os arquivos forem pequenos e vc REALMENTE não puder usar arquivos temporários (por quê não poderia?), pois consome muito mais memória, é mais difícil de escrever, entender e manter, sem falar que não te dá a oportunidade de gravar uma cópia de segurança do arquivo original e pode confundir outros processos tentando ler o arquivo que você está editando. Se, por outro lado, você puder GARANTIR que os tamanhos dos campos editáveis serão fixos (e substituidos pela mesma quantidade de bytes do mesmo tamanho), aí sim dá pra fazer algo bacana com o trio seek/read/print de forma eficiente e sem usar arquivos temporários. Mas isso normalmente só é verdade para arquivos binários. Para mais informações: perldoc -f funcao (onde "funcao" seja uma das que foram usadas acima, tipo seek, tell e truncate). Para mais informações sobre lockfiles: perldoc -f flock Bom, é isso. A solução 1 é a mais prática, mas não escala bem. Particularmente, prefiro a solução número 4, pelos motivos já citados. Se essa foi realmente a sua pergunta, taí. Agora, se não foi, espero que pelo menos ajude alguém :) Finalmente, mais uma dica: essas e muitas outras "receitas" podem ser encontradas no fantástico livro "Perl Cookbook" do Tom Christiansen e do Nathan Torkington. Foi um dos meus primeiros livros de Perl e um dos poucos que vira e mexe ainda consulto. Está todo muito bem dividido em grupos de problemas (string, arquivos, regex,...) e vc encontra rapidamente o que quer olhando o índice. Cada problema vem com a solução e uma discussão em cima do problema, de forma bastante didática. Versão Eletrônica (apenas Kindle): ----------------------------------------------- R$ 48,68 na amazon.com.br http://www.amazon.com.br/Perl-Cookbook-COOKBOOKS-ebook/dp/B0043GXMTS/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1354942271&sr=1-1 USD$ 22,79 na amazon.com http://www.amazon.com/Perl-Cookbook-COOKBOOKS-ebook/dp/B0043GXMTS/ref=sr_1_1?ie=UTF8&qid=1354942175&sr=8-1&keywords=perl+cookbook+kindle Versão Eletrônica (vários formatos pra escolher): ----------------------------------------------------------------- USD$ 19,99* na loja da O'Reilly http://shop.oreilly.com/product/9780596003135.do *USD$ o preço real da versão eletrônica é 39,99. Como vc faz parte de um grupo de usuários filiado à O'Reilly, é só usar o código de desconto "DSUG" e ganhar 50% de desconto para ebooks (40% para livros impressos). R$48,49 na Cultura http://www.livrariacultura.com.br/scripts/resenha/resenha.asp?nitem=17405530&sid=12225113814128111046650811 Versão Impressa: ------------------------ - não encontrei na Saraiva - na Cultura tem sob encomenda, à R$144,40 (+frete) - na Estante Virtual só tem 1 exemplar, à R$90,00 (+frete) http://www.estantevirtual.com.br/arkanthum/Tom-Christiansen-e-Nathan-Torkington-Perl-Cookbook-72690537 Independente da sua escolha, é um livro que recomendo vivamente para a cabeceira de qualquer programador Perl, especialmente para quem está começando. Me ajudou bastante e tenho certeza que vai ajudar você também. Acredite: vale *cada* centavo. Qq dúvida, como sempre, estamos aí! []s -b _______________________________________________ Rio-pm mailing list Rio-pm@pm.org http://mail.pm.org/mailman/listinfo/rio-pm