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

rj123
10-03-2008, 21:01
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...

rj123
11-03-2008, 01:43
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.

Kayvlim
11-03-2008, 01:46
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

reise
11-03-2008, 16:02
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"

reise
11-03-2008, 17:44
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

Kayvlim
11-03-2008, 21:25
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

Kayvlim
13-03-2008, 20:37
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

Kayvlim
14-03-2008, 17:10
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 ;)