O uso de mapeamento de memória para arquivos grandes em Delphi
Eu raramente uso o mapeamento de memória de arquivos grandes. Encontrei esse requisito, então gravei o processo para lhe dar uma introdução. Como o aplicativo não é complicado, pode haver coisas que não podem ser consideradas.
Para alguns arquivos pequenos, fluxos de arquivos comuns podem ser usados para resolver o problema. No entanto, para arquivos muito grandes, como 2G ou mais, os fluxos de arquivos não funcionarão, então você precisa usar os métodos relacionados ao mapeamento de memória da API, mesmo. se for um mapeamento de memória, o tamanho inteiro do arquivo também não pode ser mapeado de uma só vez; portanto, o mapeamento deve ser feito em partes, processando uma pequena parte de cada vez.
Vejamos algumas funções primeiro
CreateFile: abre um arquivo
GetFileSize: obtém o tamanho do arquivo
CreateFileMapping: Criar mapeamento
MapViewOfFile: arquivo de mapeamento
Olhando para a ajuda do MapViewOfFile, seus dois últimos parâmetros precisam ser um múltiplo inteiro da granularidade da página. Geralmente, a granularidade da página da máquina é de 64k (65.536 bytes). Qualquer posição, qualquer comprimento é possível, portanto, algum processamento precisa ser feito.
A tarefa deste exemplo é ler os valores de comprimento sequencialmente de uma lista de comprimento (FInfoList) e, em seguida, ler os dados do comprimento especificado sequencialmente de outro arquivo grande (FSourceFileName). Se for um arquivo pequeno, isso é mais fácil. para lidar. Basta ler o fluxo do arquivo uma vez e depois lê-lo em sequência. Para arquivos grandes, precisamos alterar constantemente a posição do mapeamento para obter os dados que desejamos.
Este exemplo mostra que a granularidade da página é obtida primeiro por meio de GetSystemInfo e, em seguida, 10 vezes a granularidade da página é usada como um bloco de dados mapeados. No loop for, será avaliado se o comprimento de leitura (totallen) mais o comprimento a ser lido. estão dentro deste tempo Dentro do intervalo de mapeamento (10 vezes a granularidade da página), se estiver, continue lendo. Se for excedido, os dados restantes devem ser registrados e, em seguida, o próximo bloco de memória deve ser remapeado e os dados restantes registrados devem ser mesclados. na nova leitura Os dados obtidos são um pouco complicados (talvez minha ideia seja muito complicada),
O código está listado abaixo.
procedimento TGetDataThread.DoGetData var FFile_Handle:THandle; FFile_Map:THandle;= TMemoryStream.Create; lista TMemoryStream.Create; //Obter informações do sistema GetSystemInfo(sysinfo); //Tamanho do bloco de granularidade de alocação de página := sysinfo.dwAllocationGranularity; //Abrir arquivo FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); FFile_Handle = INVALID_HANDLE_VALUE then Exit; //Obter tamanho do arquivo filesize := GetFileSize(FFile_Handle,nil); //Criar mapeamento FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); //Aqui mapeamos 10 vezes o tamanho do bloco em um bloco de dados. Se o tamanho do arquivo for menor que 10 vezes o tamanho do bloco, todo o comprimento do arquivo será mapeado diretamente se o tamanho do arquivo div tamanho do bloco > 10 then readlen := 10*blocksize else readlen :=. tamanho do arquivo; para i := 0 para FInfoList.Count - 1 começa list.Delimiter := ':'; //Pegue o comprimento, fiz a análise aqui, pois as informações que armazenei são do tipo a:b:c, então len é separado por ::= StrToInt(list.Strings[1]); .Strings[2]); if (i = 0) ou (totallen+len >=readlen) então comece //Se o comprimento de leitura mais o comprimento a ser lido for maior que 10 vezes o tamanho do bloco, então precisamos reter o conteúdo no final do mapeamento anterior para mesclá-lo com o novo conteúdo do mapeamento se i > 0 então começar offset := offset + readlen ; //Escrever no fluxo temporário tstream.Write(p^,readlen-totallen); //Se o comprimento dos dados não lidos não for suficiente para uma alocação; granularidade e, em seguida, mapeie diretamente o comprimento restante se filesize-offset <blocksize then readlen := filesize-offset; //Mapeamento, p é um ponteiro para a área de mapeamento //Observe que o terceiro parâmetro aqui é sempre definido como 0. Este valor deve ser definido de acordo com a situação real p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)); //Se houver dados no fluxo temporário, eles precisam ser mesclados se tstream.Size > 0 então começar; //Copie os dados do fluxo temporário sobre stream.CopyFrom(tstream,tstream.Size); //Em seguida, escreva os novos dados no final e mescle para concluir stream.Write(p^,len-tstream.Size); - tstream.Size; //Move a posição do ponteiro para apontar para o início do próximo dado Inc(p,len-tstream.Size); totallen := totallen + len Inc(p,len); end; stream.Position := 0; //Salve o fluxo em um arquivo stream.SaveToFile(IntToStr(i)+'.txt'); Grátis; tstream.Free;
Se você tiver alguma dúvida, deixe uma mensagem ou vá até a comunidade deste site para se comunicar e discutir. Obrigado pela leitura, espero que possa ajudar a todos.