View Full Version : Abrir um TXT enorme
droidman 10-03-2008, 18:55 Alguem sabe alguma maneira de procurar dentro de um TXT de grandes dimensões algum tipo de string sem ter de o abrir completamente?
Por exemplo, um ficheiro de 200mb em txt com milhões de strings, e quero procurar uma, em vez de carregar essa string toda para a memória e por o pc atafulhado de palha, e caso a string exista ele mostra o resultado completo da linha onde ela se encontra.
A ideia era fazer isto em VB6
agradecia qualquer ajuda
Ora bem, isto é um pseudo-codigo, com algumas semelhanças com o java.
boolean procurarString(File file, String s){
char[] chave = s.toChar();
boolean encontrou = false;
char c = '\0';
do{
if(c!=chave[0]){
c = toChar(file.readByte());
}
int i;
for(i=0; i<chave.length && chave[i]==c; i++){
c = toChar(file.readByte());
}
if(i==chave.length){
encontrou=true;
}
}while(c!=File.eof() && !encontrou);
}
droidman 10-03-2008, 23:52 ele assim precorre todo o ficheiro limpando a variavel constantemente, talvez seja rapido mas nem vejo se da para melhorar... vou testar
ja agora, como é que uma base de dados procura um campo ? ha bases de dados com gigas e os dados são encontrados muito rápidamente...
Dá para melhorar, pois foi feito por mim e à pressão :p
Podes ler isto:
http://en.wikipedia.org/wiki/String_searching_algorithm
http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm
São algoritmos eficientes para procura de strings.
Nas bases de dados é diferente, os campos normalmente estão ordenados, facilitando a pesquisa. Neste caso usa-se a pesquisa binaria que é bastante rápida.
Os dados estão indexados. Não tens esse controlo no plain text ;)
Quanto ao que queres, a forma mais simples que me parece ser é a mais básica de todas
open ficheiro for input as #1
do until eof(1)
line input #1, buffer
if instr(buffer, procurar) then
msgbox buffer
end if
loop
close #1
(escrito à mão sem testar, mas deve funcionar)
Não serve? Há formas mais rápidas, mas envolvem APIs que nem eu entendo :x
droidman 11-03-2008, 15:30 sim angelofwisdom eu vou usar isto parece-me ser a forma mais rápida e fácil.
uma vez encontrei o metodo mais incrivel foi o REPLACE e era muito rapido. usei para apagar repetidos num ficheiro de dimensões assusatdoras, usava algo do genero, if string = X then replace x era mais ou menos isto, numa textbox, ou seja, toda a string igual aquilo era apagada, o problema é que as vezes ele apagava letras por serem iguais, tem de ser mesmo strings exatas. o replace tambem é bom para apagar paragrafos de ficheiros onde nao pode haver qualquer tipo de paragrafo. tenha que tamanho ele tiver. O problema sao as limitações de memoria impostas pelo Vb6 às textboxes e listboxes
Se tem de ser programado lê blocks de dados de cada vez, nunca byte a byte, nunca.
Se só queres procurar a palavra arranja uma máquina unix e faz: cat ficheiro.txt | grep PALAVRA. Se só tiveres windows vai aqui http://www.wingrep.com/
droidman 11-03-2008, 16:33 Se tem de ser programado lê blocks de dados de cada vez, nunca byte a byte, nunca.
Se só queres procurar a palavra arranja uma máquina unix e faz: cat ficheiro.txt | grep PALAVRA. Se só tiveres windows vai aqui http://www.wingrep.com/
o programa é para correr em windows, embora para mim eu tenha ubuntu no portatil aqui ao lado.
Já agora, o código que me deram, procura em case sensitive? era pena...
droidman 11-03-2008, 16:40 Os dados estão indexados. Não tens esse controlo no plain text ;)
Quanto ao que queres, a forma mais simples que me parece ser é a mais básica de todas
open ficheiro for input as #1
do until eof(1)
line input #1, buffer
if instr(buffer, procurar) then
msgbox buffer
end if
loop
close #1
(escrito à mão sem testar, mas deve funcionar)
Não serve? Há formas mais rápidas, mas envolvem APIs que nem eu entendo :x
É case sensitive! :(
ha alguma forma de dar a volta a isto em VB ? eu quero que ele encontre resultados tanto para strings como "CARLOS" como "carlos" ou "CaRLoS"
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadText {
public static void main(String[] args) throws IOException {
try {
BufferedReader in = new BufferedReader(new FileReader(args[0]));
String str;
while ((str = in.readLine()) != null) {
if (str.toUpperCase().contains(args[1].toUpperCase())) {
System.out.println("Encontrou:"+str);
}
}
in.close();
} catch (IOException e) {
}
}
}
Lê linha a linha e não olha ao case.
primeiro parametro é o nome do ficheiro o segundo é a palavra a procurar
droidman 11-03-2008, 19:49 import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ReadText {
public static void main(String[] args) throws IOException {
try {
BufferedReader in = new BufferedReader(new FileReader(args[0]));
String str;
while ((str = in.readLine()) != null) {
if (str.toUpperCase().contains(args[1].toUpperCase())) {
System.out.println("Encontrou:"+str);
}
}
in.close();
} catch (IOException e) {
}
}
}
Lê linha a linha e não olha ao case.
primeiro parametro é o nome do ficheiro o segundo é a palavra a procurar
isso é capaz de funcionar mas é java e o meu programa está todo em VB6
Se olhares para o código dele, ele faz o que eu te ia aconselhar agora: comparar as strings em MAIÚSCULAS :)
Ou seja, para ser case-insensitive, troca
if instr(buffer, procurar) then
por
if instr(ucase(buffer), ucase(procurar)) then
;)
droidman 11-03-2008, 22:04 Se olhares para o código dele, ele faz o que eu te ia aconselhar agora: comparar as strings em MAIÚSCULAS :)
Ou seja, para ser case-insensitive, troca
if instr(buffer, procurar) then
por
if instr(ucase(buffer), ucase(procurar)) then
;)
funciona na perfeição, meti uma checkbox para case sensitive e pesquizas não case sensitive. o programa neste momento verifica se ha strings iguais ás inseridas numa textbox num ficheiro que tenho com 7.8 milhões de strings (aprox 370mb em txt) em 35 a 40 segundos num core 2 duo 2.13ghz
|[-BooT-]| 11-03-2008, 22:26 já agora se me permites a pergunta, porque é que tens essa informação toda num txt?
droidman 12-03-2008, 00:22 |;2660425']já agora se me permites a pergunta, porque é que tens essa informação toda num txt?
a principio fiz um programa parecido com este para um amigo que eliminava e-mails repetidos de ficheiros em TXT que ele extraia do servidor de mails dele e depois é que me disse k o fazia assim, ele nao sabia que ao fazer o dump do SQL bastava por lá que so queria resultados unicos mas pronto tive imenso trabalho mas ao menos aprendi.
Este programa tou a usa-lo para encontrar chaves MD5 em ficheiros txt grandes...
Tem algumas funcionalidades... :)
pedrotuga 13-03-2008, 12:45 Tipo, o grep faz precisamente isso.
Se usas um sistema operativo baseado em unix ( linux, bsd, macos, etc ) abre uma linha de comandos e..
man grep
Se fores utilizador do windows tens que instalar o grep. Google -> grep windows
Se for para um trabalho académico, podes abrir o ficheiro linha a linha ou caracter a caracter, isso é perfeitamente possivel em praticamente todas as linguagens.
No teu caso dá-te jeito ser linha a linha.
Em perl é particularmente facil de fazer isso.
$FICHEIRO = "grandeficheiro.txt";
open(FICHEIRO) or die("Could not open log file.");
$atuastring = "qq coisa a procurar aqui";
$i=1;
foreach $line (<FICHEIRO>) {
chomp($line); # remove the newline from $line.
if ($line =~ /$atuastring/){
print "Linha ".$i.":".$line;
}
$i++;
}
Não testei este código mas deve funcionar
droidman 13-03-2008, 15:23 Tipo, o grep faz precisamente isso.
Se usas um sistema operativo baseado em unix ( linux, bsd, macos, etc ) abre uma linha de comandos e..
man grep
Se fores utilizador do windows tens que instalar o grep. Google -> grep windows
Se for para um trabalho académico, podes abrir o ficheiro linha a linha ou caracter a caracter, isso é perfeitamente possivel em praticamente todas as linguagens.
No teu caso dá-te jeito ser linha a linha.
Em perl é particularmente facil de fazer isso.
$FICHEIRO = "grandeficheiro.txt";
open(FICHEIRO) or die("Could not open log file.");
$atuastring = "qq coisa a procurar aqui";
$i=1;
foreach $line (<FICHEIRO>) {
chomp($line); # remove the newline from $line.
if ($line =~ /$atuastring/){
print "Linha ".$i.":".$line;
}
$i++;
}
Não testei este código mas deve funcionar
por acaso procurei um bocado e nao encontrei como por isto em vb. nem sei como carregar o grep, talvez seja por componenete, n sei bem, nao o encontro nos componentes
pedrotuga (http://www.techzonept.com/member.php?u=12290), ele está em VB, logo, num sistema Windows ;)
Já agora, quanto ao teu código em Perl, para a tua regexp resultar, em vez de
if ($line =~ /$atuastring/){
devias era ter algo como
if ($line =~ /$atuastring/i){
de modo a ser case insensitive ;)
slack_guy 13-03-2008, 22:39 pedrotuga, ele está em VB, logo, num sistema Windows
Nesse caso pode usar ActivePerl.
Já agora, quanto ao teu código em Perl, para a tua regexp resultar, em vez de
Código:
if ($line =~ /$atuastring/){
devias era ter algo como
Código:
if ($line =~ /$atuastring/i){
de modo a ser case insensitive
Em boa verdade, até podia escrever assim:
#!/usr/bin/perl
use strict;
use warnings;
my $f = q|/path/to/file|;
my $str = q|string|;
open my $F,'<',$f or die "$!\n";
while (<$F>) { printf "%.8d %s", $., $_ if /$str/im; }
close $F;
Aliás, como se trata de Perl, até podia escrever de muitas outras formas :-)
droidman 13-03-2008, 22:58 bem a ideia era isto ficar incluido no meu software em GUI e nao linha de comandos pk o programa vai ser usado por pessoas que percebem pouco... de qualquer forma ele ja funciona está a ler cerca de 200mb a cada 35 a 40 segundos e está feito em VB6 nao me parece uma média muito acima dos valores que eu pretendia. cada ficheiro de 150mb tem aproximadamente 7 milhões de linhas. apenas fica a minha duvida, se ha outra forma de eu incorporar os dados sem ser num txt e sim num tipo de ficheiro que agora nao me venha à memória e que acelere isto
Armadillo 14-03-2008, 11:40 sempre podes recorrer a uma bd em access.
acho que ainda podes optimizar mais o teu codigo para 25/35seg no maximo. ve aqui (http://www.aivosto.com/vbtips/stringopt.html)alguns conselhos.
cumps
edit: tem em conta que as maquinas dos utilizadores podem nao ser as melhores...
slack_guy 14-03-2008, 12:14 de qualquer forma ele ja funciona está a ler cerca de 200mb a cada 35 a 40 segundos
Só para teres uma ideia:
#!/usr/bin/perl
use strict;
use warnings;
my $f = q|./access_1_log|; # este ficheiro tem 3,491,509 linhas e 731Mb
my $str = q|85.240.227.117|;
open my $F,'<',$f or die "$!\n";
while (<$F>) { printf "%.8d %s", $., $_ if /$str/im; }
close $F;
# time perl perl_find.pl
.....
perl perl_find.pl 4,78s user 0,30s system 99% cpu 5,093 total
Ou seja, qualquer coisa como 5 segundos.
Epílogo: às vezes podemos não estar a usar as ferramentas mais adequadas para determinados fins.
BTW, Perl = Practical Extraction and Report Language, isto é, exactamente aquilo que queres fazer ;-)
Armadillo 14-03-2008, 12:22 outra soluçao é partires o ficheiro em blocos (nao sei é como) de 50 mb e depois com multithreading (é possivel em vb6 que eu ja fiz:p), pesquisares nestes ficheiros temporarios. por quantos mais ficheiros dividires,mais rapida se torna a pesquisa (desde que nao ocupes 100% do cpu)
droidman 14-03-2008, 12:40 outra soluçao é partires o ficheiro em blocos (nao sei é como) de 50 mb e depois com multithreading (é possivel em vb6 que eu ja fiz:p), pesquisares nestes ficheiros temporarios. por quantos mais ficheiros dividires,mais rapida se torna a pesquisa (desde que nao ocupes 100% do cpu)
é possível e é fácil. Partir os ficheiros não dá trabalho nenhum é fazer uma coisa do genero
dim contador as long, temp as string, total as string
Open ficheiro For input As #1
Do While Not EOF(1)
'ciclo for ou if etc aqui qualquer coisa serve, kto mais leve melhor
contador = contador +1
Line Input #FF, temp
total = total & string 'assumindo k ele ja desce de linha sozinho senao mete-se um vbCrLf
if contador >= 1000000 then ' (um milhao deverão ser prai 40 a 60mb se tiver as strings como eu as meti)
contador = 0
Open FicheiroDeSaida For Output As #2
- - - - -
o codigo da praxe para guardar
- - - - -
end if
loop
Nem testei mas deve ser algo proximo disso.
Ah uma coisa k fiz para evitar o crash dos pcs foi algo k até descobrir, podendo ser simples, me dava cabo da vida; o DoEvents faz com que as coisas em VB6 fiquem lentas, muito lentas mesmo, entao fiz algo do genero uma variavel contadora e se ela fosse igual a X ela fazia doevents e a variavel era defenida numa textbox pelo user e pode ser alterada duranto o processo, algo do tipo:
if contador = text1.text then
DoEvents
contador = 0
end if
E no text1.text pode-se por uma validação do genero Text1_change ou on change para evitar que alguem meta la uma letra e rebente o programa quando ele ja vai em horas de trabalho.
Este DoEvents faz com que o programa mesmo k esteja crashado seja trazido ao user denovo vindo dos mortos e mostre onde está e o que se passa em vez de ficar constantemente a fazer o DoEvents ou constantemente Empenado sem se saber se ele está a trabalhar ou se craxou de vez.
Ah, quanto aos codigos em perl, eu podia ate por o portatil k tenho aki a correr isso mas keria fazer interface e ja envolvia site e transformar a makina num servidor. assim klker um a podia usar sem ter k andar a mexer em ssh
slack_guy 14-03-2008, 13:39 Ah, quanto aos codigos em perl, eu podia ate por o portatil k tenho aki a correr isso mas keria fazer interface e ja envolvia site e transformar a makina num servidor. assim klker um a podia usar sem ter k andar a mexer em ssh
Perl em Windows é ActivePerl (http://www.activestate.com/Products/?_x=1). Não precisas de servidor web nem de SSH. AH! E também podes ter interface gráfica :-)
droidman 14-03-2008, 13:50 Perl em Windows é ActivePerl (http://www.activestate.com/Products/?_x=1). Não precisas de servidor web nem de SSH. AH! E também podes ter interface gráfica :-)
e da para por isso a funcionar interligado com alguma coisa em php?
slack_guy 14-03-2008, 15:14 Interligado com o que tu quiseres. Só precisas de instalar o Perl (em windows, ActivePerl).
droidman 14-03-2008, 15:15 Interligado com o que tu quiseres. Só precisas de instalar o Perl (em windows, ActivePerl).
obrigado pela informação, falta eu saber fazer essa ligação visto a minha programação em php ser do mais rasca que ha, sei muito pouco para um projecto destes
Nesse caso pode usar ActivePerl.
Eu sei :P eu uso o ActivePerl. Referia-me ao grep.
obrigado pela informação, falta eu saber fazer essa ligação visto a minha programação em php ser do mais rasca que ha, sei muito pouco para um projecto destes
Olha que Perl e PHP são bastante semelhantes ;)
|
|