Jump to content
strajk-

Apagar Elementos de um Array

Recommended Posts

strajk-

Boa tarde, meu nome é Steven e tenho uma pequena dúvida.

Estive a navegar pela google mas não encontro solução a meu problema que é apagar registos de um array.

Vamos supor que eu tenho este bocado de código:

TYPE
aluno=RECORD
              nome:string;
              idade:integer;
              morada:string;
              curso:string;
              nota:integer;
            end;
alu=array[1..1000] of aluno;

VAR
al:alu;

Vai ser pedido ao utilizador para introduzir o nome do aluno, para apagar todos os dados do aluno deste array.

O que não estou a perceber é como vai ser o código para apagar os registos do determinado aluno.

Tinha encontrado algo parecido do que eu queria na net mas não percebi bem aquele sistema:

http://www.daniweb.com/software-development/pascal-and-delphi/threads/99785

Alguem pode explicar?

Share this post


Link to post
Share on other sites
thoga31

Bem, a minha sugestão inicial vai em encontrar qual o índice do array que tem esse aluno, e, quando o encontrar, apagar o registo. Por opção, vamos puxar os elementos daí para a frente 1 índice para trás.

array de 5 elementos, cheio:

[*]Ana

[*]Carlos

[*]Matias

[*]Steven

[*]Marco

Vamos apagar o aluno Matias.

[*]Ana

[*]Carlos

[*]Steven

[*]Marco

[*]vazio

Isto faz-se com um ciclo while ou repeat.



// vamos encontrar o aluno ParaApagar
while not(aluno[i].nome = ParaApagar) or (i < 5) do i := i + 1;

// vamos mover os registos
for j:=i+1 to 5 do begin
    aluno[j-1].nome := aluno[j].nome;
    // etc...
end;

// o último registo deverá ser apagado, pois o processo anterior não o faz
aluno[5].nome := '';
// etc...

Cumpz. :)


Knowledge is free!

Share this post


Link to post
Share on other sites
strajk-

Com este sistema ao ter uma base de dados eu teria que fazer uma contagem dos elementos que tem dentro do array, já que pode ser desconhecida os campos preenchidos dentro dela.

No seu caso temos 5 elementos, e se eu não soubesse quantos alunos la tenho?

Desde já obrigado pela resposta!

Share this post


Link to post
Share on other sites
nunopicado

Tens de ter sempre um meio de descobrir o final...

Ou mantens uma variável integer (index, por exemplo) sempre actualizada com o valor do ultimo registo, ou então tens todos os registos vazios inicializados para um determinado valor, e crias uma função que te devolva o numero do ultimo registo que exista sem estar inicializado, ou seja, o ultimo que tem valores...

PS: Acho a segunda hipotese mais segura, embora de um pouco mais de trabalho. Com ela é mais simples de não haver erros, e de corrigir os que ainda assim apareçam...


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
nunopicado

Os elementos base desta segunda hipotese:

const
      MaxRecs=1000;  // Numero máximo de registos na DB
type 
      TPessoas=record   // Campos da DB
                              nome,morada,telefone,telemovel,email:string;
                              idade:integer;
                              sexo:char;
                     end;
var
   dbPessoas:array[1..MaxRecs] of TPessoas; // Variavel da DB



procedure Inicializa(rec:integer);  
// Procedimento de inicialização da DB
// Se o parametro estiver fora dos limites da DB, ele inicializa todos os registos (por exemplo, 0)
// Se o paramentro estiver dentro dos limites da DB, ele inicializa esse registo

procedure LimpaRec(rec:integer);
// Sub-procedimento que limpa efectivamente o registo indicado
begin
       with dbPessoas[rec] do
            begin
                   nome:='';
                   morada:='';
                   telefone:='';
                   telemovel:='';
                   email:='';
                   idade:=0;
                   sexo:=#0;
            end;
end;
var
   i:integer;
begin
   if (rec>=1) and (rec<=MaxRecs) 
       then LimpaRec(Rec)
       else for i:=1 to MaxRecs do
                  LimpaRec(i);
end;


function ContaRecs:integer;
// Conta quais os registos cujos valores sejam diferentes dos valores de inicialização
var
   i,Cont:integer;
begin
   Cont:=0;
   for i:=1 to MaxRecs do
       with dbPessoas[rec] do
            if not ((nome='') and (morada='') and (telefone='') and (telemovel='') and (email='') and (idade=0) and (sexo=#0)) then inc(cont);
   ContaRecs:=Cont;
end;

procedure ApagaRec(rec:integer);
// Apaga um registo, movendo todos os seguintes uma posição para trás
// Por fim, inicializa o ultimo registo que estava ocupado (e que está agora também na penultima posição), a fim de deixar o lugar vago para um novo registo
begin
     if (rec<1) or (rec>MaxRecs) then exit;
     for i:=rec to ContaRecs-1 do
          rec[i]:=rec[i+1];
     Inicializa(ContaRecs);
end;

Não testei, mas se não me escapou nada, deve funcionar...


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
thoga31

nunopicado e os seus trechos de código maravilhosos. :)

Bem, a questão ficou bem respondida. :D


Knowledge is free!

Share this post


Link to post
Share on other sites
nunopicado
:):D

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
nunopicado

Ah, faltou uma coisa... :D

A "primeira" coisa a fazer ao iniciar o programa principal é inicializar todo o array.

para isso basta chamar:

.
.
.
begin
       Inicializa(0);
       .
       .
       .
       .
       .
end.


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
strajk-

Agradeço imenso, que já sei como isto funciona.

Agora tenho é outra dúvida que talvez seja a ultima, tem a ver com os seek.

Não sei o que esta errado neste código, mas depois de violar a tecla F8 descobri que deve ser do seek:

PROCEDURE adicionar;   {Adicionar Alunos a BD}
VAR
aaa:1..n; {variavel que indica o numero de alunos a inserir}
aa:string; {responsavel pelo ciclo REPEAT deste procedimento}
BEGIN
clrscr;
reset(f);
write('Quantos alunos deseja introduzir?: ');
readln(aaa);
REPEAT
clrscr;
 FOR i:=1 TO aaa DO  {ciclo da introdu‡Æo de alunos}
  begin
   writeln('Introduza os dados do aluno N§',i,' de ',aaa);
   write('Nome: ');
   readln(al[i].nome);
   write('Idade: ');
   readln(al[i].idade);
   write('Morada: ');
   readln(al[i].morada);
   write('Curso: ');
   readln(al[i].curso);
   write('Nota: ');
   readln(al[i].nota);
   writeln;
   writeln('SUCCESSO! Prime Enter para continuar.');
   readln;
   clrscr;
  end;
writeln('##################################################################');
writeln('##   Nome   ##   Idade   ##   Morada   ##   Curso   ##   Nota   ##');
writeln('##################################################################');
 FOR i:=1 TO aaa DO   {confirma‡Æo dos dados}
  begin
   writeln('    ',al[i].nome,'        ',al[i].idade,'         ',al[i].morada,'         ',al[i].curso,'           ',al[i].nota);
  end;
 writeln;
 write('Confirma os dados introduzidos? (S/N): ');
 readln(aa);
 seek(f,filesize(f));
 IF (aa='s') or (aa='S') or (aa='Sim') or (aa='sim') THEN
  begin
   write(f,al);
  end;
UNTIL (aa='s') or (aa='S') or (aa='Sim') or (aa='sim');
close(f);
writeln;
write('Terminou a inser‡Æo com SUCESSO, prime Enter para voltar ao menu!');
readln;
END;

Isto é apenas um excerto do que estou a fazer, depois ao criar uma Base de Dados isto é o que la tenho: http://img802.imageshack.us/img802/2921/antesp.png

Este código (PROCEDURE adicionar) supostamente tinha que adicionar um aluno na ultima posição da Base de Dados, mas infelizmente faz isto: http://img215.imageshack.us/img215/869/depoisw.png

Nem meu Professor consegue aranjar uma solução para o erro, nossa suspeita obviamente é o seek, e infelizmente com este erro nem consigo adicionar nem editar entradas com sucesso.

Alguem pode explicar o que esta de errado no código?

Muito Obrigado pelas respostas ate agora!

Adoro programação e desejo aprender o mais possivel!

Share this post


Link to post
Share on other sites
thoga31

Utiliza as tags do GeSHi, [ code=pascal ][ /code ], sem os espaços. :D

Desta vez coloquei eu, mas passa a colocá-las tu, ok?

Quanto ao erro, tenho de me debruçar mais sobre o assunto, que à primeira vista não detecto anomalias de maior, e estou um nadinha ocupado. :)

Adoro programação e desejo aprender o mais possivel!

De todos nós, quem não quer? :)


Knowledge is free!

Share this post


Link to post
Share on other sites
strajk-

Utiliza as tags do GeSHi, [ code=pascal ][ /code ], sem os espaços. :D

Desta vez coloquei eu, mas passa a colocá-las tu, ok?

Quanto ao erro, tenho de me debruçar mais sobre o assunto, que à primeira vista não detecto anomalias de maior, e estou um nadinha ocupado. :)

De todos nós, quem não quer? :)

Por acaso nem tinha reparado nas tags pascal, obrigado!

Os únicos que estão a dar conflito no meu programa é este procedure "adicionar" e o "editar" que utiliza tambem o seek.

Supostamente aquilo devia de ir para a ultima posição do Array e colocar a informação no "adicionar".

Mas é estranho porque o que esta a fazer é o seguinte:

1º Coloca os registos na Primeira posição em vez da ultima, movendo o que estava na 1,2,3,4,5,6,7...etc posição uma posição para frente.

2º O registo que estava na ultima posição tambem é movido para frente e torna-se nula em todos os campos.

Realmente isto é me um misterio porque não esta a funcionar  :wallbash:

Share this post


Link to post
Share on other sites
nunopicado

Pode não ser nada disto, até porque não conheço bem o FreePascal.

Mas no Turbo Pascal, onde eu usava os ficheiros sequenciais, era necessário definir com exactidão o tamanho das variáveis string do registo para que depois o ponteiro de posição do ficheiro funcionasse bem...

Tenta isso... Onde defines o tipo, faz tipo isto:

type
     tpessoas=record
              nome,morada:string[200];      // nas strings, indica à frente, entre parentesis rectos, qual o numero máximo de caracteres que essa string pode guardar
              telefone,telemovel,fax:string[12];
              idade:integer;
              sexo:char;
     end;


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
nunopicado

Antes de mais tira o Sr. :(

Estava aqui a ler o teu codigo e reparei que ao adicionares, vais escrever uma variavel no ficheiro que é já de si um array, é isso?

De cada vez que se adicionam alunos, les para um array e depois escreves no ficheiro?

Posso estar enganado, mas se é assim, parece-me uma receita para o desastre, e daquelas de microondas, que é para não demorar muito a estourar.

Se fosse a ti, removia o array da historia, e passava a trabalhar directamente no ficheiro.

Ficas com a variavel al:tpessoas (ou qualquer nome que tenhas dado ao tipo), e ao adicionar, les os dados para a variavel al (sem qualquer indice é claro) e gravas no ficheiro logo de seguida.

Para ler os dados do ficheiro, a mesma coisa... Les registo a registo para a variavel al, e depois é só mostrar no quadro.

A cada leitura ou escrita, fazes o close(f); para ter a certeza que o ficheiro está sempre disponivel para uso, e que mesmo que o programa seja encerrado, não fica nada em memoria...

Tens aqui um exemplo:

program testefiles;

uses
    crt;

type
tpessoa=record
          nome,morada,curso:string;
		  idade,nota:integer;
		end;

var
al:tpessoa;
f:file of tpessoa;
    i:integer;


PROCEDURE adicionar;   {Adicionar Alunos a BD}
VAR
aa:string; {responsavel pelo ciclo REPEAT deste procedimento}
BEGIN
clrscr;
REPEAT
clrscr;
  with al do
   begin
   reset(f);
    writeln('Introduza os dados do aluno N§',filesize(f)+1);
    close(f);
    write('Nome: ');
    readln(nome);
    if nome='FIM' then exit;
    write('Idade: ');
    readln(idade);
    write('Morada: ');
    readln(morada);
    write('Curso: ');
    readln(curso);
    write('Nota: ');
    readln(nota);
    writeln;
    writeln('SUCCESSO! Prime Enter para continuar.');
    readln;
    clrscr;
    writeln('##################################################################');
    writeln('##   Nome   ##   Idade   ##   Morada   ##   Curso   ##   Nota   ##');
    writeln('##################################################################');
    writeln('    ',nome,'        ',idade,'         ',morada,'         ',curso,'           ',nota);
   end;
  writeln;
  write('Confirma os dados introduzidos? (S/N): ');
  readln(aa);

UNTIL (aa='s') or (aa='S') or (aa='Sim') or (aa='sim');
reset(f);
seek(f,filesize(f));
write(f,al);
close(f);
writeln;
write('Terminou a inser‡Æo com SUCESSO, prime Enter para voltar ao menu!');
readln;
END;

procedure mostraalunos;
var
   cont:integer;
begin
     clrscr;
     writeln('##################################################################');
     writeln('##   Nome   ##   Idade   ##   Morada   ##   Curso   ##   Nota   ##');
     writeln('##################################################################');
     reset(f);
     FOR cont:=0 TO FILESIZE(F)-1 DO
          BEGIN
               SEEK(F,cont);
               READ(F,al);
               with al do
                    writeln('    ',nome,'        ',idade,'         ',morada,'         ',curso,'           ',nota);

          end;
     close(f);
end;


begin
assign(f,'c:\testefile.dat');
    {$i-}
    reset(f);
    {$i+}
    if ioresult<>0 then rewrite(f);
close(f);
adicionar;


    MostraAlunos;
    readln;

end.


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
pwseo

Olá Steven e restantes membros,

Sei que venho tarde, mas mesmo que não venha a tempo de introduzir alterações no programa, venho sempre a tempo de contribuir para o conhecimento dos outros.

Antes de mais, um esclarecimento: as arrays em pascal são coisas fixas, não podemos alterar o seu tamanho, o que implica a impossibilidade de apagar membros das mesmas.

Penso que a solução ao problema colocado passa por duas coisas:

    [*]utilizar uma array dinâmica

    [*]incluir um campo booleano "deleted" no teu record aluno

    Utilização de arrays dinâmicas

    As arrays dinâmicas apareceram no Delphi, e penso que também existem no FreePascal. Estas arrays são declaradas sem especificar o tamanho das mesmas, e definimos a sua dimensão ao longo da execução do programa:

    var
      a: array of TAluno;
    begin
      setlength(a, 0);
      // ...
      setlength(a, Length(a) + 20);  // mais 20 alunos
      // ...
    end;

    O campo "deleted"

    A inclusão de um campo deleted (booleano, claro) nos registos dos alunos facilita a gestão da base de dados. Simplesmente teremos que ter em conta esse campo sempre que escrevamos dados, seja no ecrã ou num ficheiro.

    type
      TAluno = record
        nome: string[60];
        morada: string[100];
        curso: string[40];
        idade: byte;
        nota: byte;
        deleted: boolean;
      end;

    Reparem que defini a dimensão das strings todas. Como o nunopicado disse num dos posts, as strings precisam de uma dimensão para as lermos ou escrevermos em ficheiros. No entanto, não precisamos de ser nós a indicar essa dimensão - se não o fizermos, o compilador assume 255 caracteres + 1 byte para indicar o tamanho da string (total de 256bytes por string: um desperdício).

    Sabendo que tendo 3 strings de 256bytes em 1000 registos (vi nos posts uma array com dimensão 1000) o programa reserva de 750KB de memória... para usar apenas uma fracção da mesma.

    Continuando, depois de termos o campo deleted, sempre que fizermos algo com os alunos teremos que ter em atenção esse campo:

    procedure FazerAlgo;
    var
      i: integer;
    begin
      for i := 0 to length(alunos) do
      begin
        if alunos[i].deleted = true then
          continue; // saltamos este aluno porque tecnicamente não existe
        // ...
      end;
    end;

    Desta forma, apagar um aluno seria tão simples quanto isto:

    procedure ApagarAluno(idx: integer);
    begin
      alunos[idx].deleted := true;
      dec(num);
    end;

    thoga31, falaste em mover todos os alunos um índice para trás (e disseste muito bem "por opção"). Isto em programas com muitos registos seria impensável, dado que apagar o aluno #0 numa base de dados de 10000 alunos implicaria 9999 operações para "reordenar" a array.

    Neste caso isso nunca aconteceria, claro, mas não faz mal pensarmos sempre nestas coisas; ajuda-nos a fazer código mais robusto.

    Nessas situações, utiliza-se algo como falei, o campo deleted, uma vez que é preferível gastar mais um byte em memória do que sacrificar a performance. Além disso esse byte pode ser tirado de uma das strings.

    Guardar os dados num ficheiro

    procedure LerAlunos(fname: string);
    var
      out: file of TAluno;
      i: integer;
    begin
      assign(out, fname);
      rewrite(out);
    
      for i := to length(alunos) do
        if not alunos[i].deleted then
          write(out, alunos[i]);
    
      close(f);
    end;

    Leitura de alunos a partir de um ficheiro

    procedure GuardarAlunos(fname: string);
    var
      in: file of TAluno;
      i: integer;
    begin
      assign(in, fname);
      reset(in);
    
      while (not eof(in)) do
      begin
        // aumentamos a array global 'alunos' por
        // 30 de cada vez. Assim não desperdiçamos
        // 950 records por só termos 50.
        if count = length(alunos) then
          setlength(alunos, length(alunos) + 30);
    
        // lemos o record actual para o fim da array
        read(in, alunos[count]);
    
        // aumentamos o contador
        inc(i);
        close(in);
      end;
    end;

    Quanto ao que o nunopicado falou de tirar a array da equação e trabalhar directamente no ficheiro, isso depende muito do tipo de utilização que se vai dar ao programa, mas no geral é boa ideia ter pelo menos parte dos dados em memória.

    É muito chato um programa ter que fazer IO só para ver o registo seguinte, que ocupa ~200 bytes... Como aqui é desnecessário implementar leitura em blocos, penso que o mais acertado é mesmo ler tudo para a memória no início do programa e escrever no final. Não é um programa que esteja sujeito a muita instabilidade também. Mas concordo com o que dizes, numa forma mais abrangente, cujo corolário é que gravar os dados frequentemente e fechar os ficheiros (close(f);) para evitar perdas. A memória é volátil.

    Já agora, aqui fica o código para adicionar um aluno à base de dados, durante a execução:

    procedure AdicionarAluno;
    begin
      if count = length(alunos) then
        setlength(alunos, length(alunos + 30);
    
      write('Nome: ');
      readln(alunos[length(alunos)].nome);
    
      // ...
    
      alunos[length(alunos)].deleted := false; // importante!
    
      inc(count);
    end;

    NOTA: nas snippets acima assumi a existência de uma variável global alunos: array of TAluno e uma count: integer onde é armazenado o número de alunos.

    Outra coisa, não testei nenhum deste código. Por isso é que existem tantos fragmentos no post... foi escrito à medida que respondia (ou seja, pode haver erros de sintaxe).

Share this post


Link to post
Share on other sites
thoga31

pedro-kun, coloca essas tuas explicações na Wiki, dão um excelente mini-tutorial, e lá terão a necessária visibilidade, e aqui fica algo "perdido". :thumbsup:


Knowledge is free!

Share this post


Link to post
Share on other sites
pwseo

Essa questão da visibilidade é algo a ser debatido. Penso que isto para já fica melhor aqui como um simples post, ainda por cima é muito específico ao problema de quem começou a thread.

No futuro, se me der para generalizar isto, talvez coloque na wiki.

O pessoal do pascal parece-me relativamente assíduo nas threads... e na wiki é menos provável que as pessoas discutam isto :)

Share this post


Link to post
Share on other sites
thoga31

Essa questão da visibilidade é algo a ser debatido. Penso que isto para já fica melhor aqui como um simples post, ainda por cima é muito específico ao problema de quem começou a thread.

No futuro, se me der para generalizar isto, talvez coloque na wiki.

O pessoal do pascal parece-me relativamente assíduo nas threads... e na wiki é menos provável que as pessoas discutam isto :)

Estás muito terra-a-terra no fórum, ou seja, não sais daqui e expandes para a Wiki.

Na Wiki pode ser discutido, ou para que é que a administração da Wiki teve o trabalho de instalar o Discussion Plugin?

E esta informação útil aqui fica "perdida" - e a Wiki precisa deste género de informações para crescer! Assim o que escreveste deixa de estar limitado a um user e passa a estar disponível para centenas de utilizadores da Wiki.

Mais, a explicação que deste é muito fácil de se adaptar, pois até está grosso modo generalista. :thumbsup:

P.S. - Não sejam resistentes à Wiki! O P@P não é só o fórum!


Knowledge is free!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.