การใช้การแมปหน่วยความจำสำหรับไฟล์ขนาดใหญ่ใน Delphi
ฉันไม่ค่อยได้ใช้การแมปหน่วยความจำของไฟล์ขนาดใหญ่ ฉันบังเอิญเจอข้อกำหนดดังกล่าว ดังนั้นฉันจึงบันทึกขั้นตอนไว้เพื่อให้คุณได้ทราบ เนื่องจากแอปพลิเคชันไม่ซับซ้อน จึงอาจมีสิ่งที่ไม่สามารถพิจารณาได้
สำหรับไฟล์ขนาดเล็กบางไฟล์ การสตรีมไฟล์ธรรมดาสามารถใช้เพื่อแก้ไขปัญหาได้ อย่างไรก็ตาม สำหรับไฟล์ขนาดใหญ่มาก เช่น 2G ขึ้นไป การสตรีมไฟล์จะไม่ทำงาน ดังนั้น คุณจึงจำเป็นต้องใช้วิธีการที่เกี่ยวข้องกับการแมปหน่วยความจำของ API แม้กระทั่ง หากเป็นการแมปหน่วยความจำ และไม่สามารถแมปขนาดไฟล์ทั้งหมดพร้อมกันได้ ดังนั้นการแมปจะต้องดำเนินการเป็นชิ้น ๆ โดยประมวลผลส่วนเล็ก ๆ ในแต่ละครั้ง
มาดูฟังก์ชั่นบางอย่างกันก่อน
CreateFile: เปิดไฟล์
GetFileSize: รับขนาดไฟล์
CreateFileMapping: สร้างการแมป
MapViewOfFile: ไฟล์การแมป
เมื่อพิจารณาจากความช่วยเหลือของ MapViewOfFile พารามิเตอร์สองตัวสุดท้ายจะต้องเป็นจำนวนเต็มเท่าของความละเอียดของหน้า โดยทั่วไปรายละเอียดของหน้าของเครื่องคือ 64k (65536 ไบต์) อย่างไรก็ตาม ในการใช้งานจริงของเรา โดยทั่วไปจะไม่เป็นเช่นนั้น . ตำแหน่งใดก็ได้ ความยาวเท่าใดก็ได้ ดังนั้นจึงจำเป็นต้องดำเนินการบางอย่าง
งานของตัวอย่างนี้คือการอ่านค่าความยาวตามลำดับจากรายการความยาว (FInfoList) จากนั้นอ่านข้อมูลที่มีความยาวที่ระบุตามลำดับจากไฟล์ขนาดใหญ่อื่น (FSourceFileName) หากเป็นไฟล์ขนาดเล็กก็จะง่ายกว่า ในการจัดการ เพียงแค่อ่านไฟล์สตรีมหนึ่งครั้งแล้วอ่านตามลำดับสำหรับไฟล์ขนาดใหญ่เราจำเป็นต้องเปลี่ยนตำแหน่งการแมปอย่างต่อเนื่องเพื่อให้ได้ข้อมูลที่เราต้องการ
ตัวอย่างนี้แสดงให้เห็นว่ารายละเอียดของหน้าได้รับครั้งแรกผ่าน GetSystemInfo จากนั้น 10 เท่าของรายละเอียดของหน้าจะถูกใช้เป็นบล็อกข้อมูลที่แมป ใน for loop นั้นจะถูกตัดสินว่าความยาวการอ่าน (ผลรวม) บวกกับความยาวที่จะอ่านหรือไม่ อยู่ภายในเวลานี้ ภายในช่วงการแมป (10 เท่าของความละเอียดของหน้า) หากเป็นเช่นนั้น ให้อ่านต่อ หากเกินนั้น จะต้องบันทึกข้อมูลที่เหลือ จากนั้นจะต้องทำการแมปบล็อกถัดไปของหน่วยความจำ และข้อมูลที่เหลือที่บันทึกไว้จะต้องถูกรวมเข้าด้วยกัน เข้าสู่การอ่านใหม่ ข้อมูลที่ได้รับค่อนข้างซับซ้อน (บางทีความคิดของฉันซับซ้อนเกินไป)
รหัสอยู่ด้านล่าง
ขั้นตอน TGetDataThread.DoGetData; var FFile_Handle; FFile_Map:THandle; p:PCharr; i,interval:Integer; TMemoryStream.Create; รายการ := TStringList.Create; //รับข้อมูลระบบ GetSystemInfo(sysinfo); //ขนาดการจัดสรรหน้า := sysinfo.dwAllocationGranularity; //เปิดไฟล์ FILE_ATTRIBUTE_NORMAL,0); FFile_Handle = INVALID_HANDLE_VALUE จากนั้นออก; // รับขนาดไฟล์ := GetFileSize(FFile_Handle,nil); // สร้างการแมป FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); // ที่นี่ เราได้แมปขนาดบล็อก 10 เท่าเป็นบล็อกข้อมูลเดียว หากขนาดไฟล์น้อยกว่า 10 เท่าของขนาดบล็อก ความยาวไฟล์ทั้งหมดจะถูกแมปโดยตรงหากขนาดไฟล์ div บล็อก > 10 แล้ว readlen := 10*ขนาดบล็อก อื่น readlen := ขนาดไฟล์ สำหรับ i := 0 ถึง FInfoList.Count - 1 ทำรายการเริ่มต้น ตัวคั่น := ':'; list.DelimitedText := FInfoList.Strings[i]; //ดูความยาว ฉันทำการวิเคราะห์ที่นี่ เนื่องจากข้อมูลที่ฉันเก็บไว้เป็นประเภท a:b:c ดังนั้น len จึงถูกคั่นด้วย ::= StrToInt(list.Strings[1]); .Strings[2]); ถ้า (i = 0) หรือ (totallen+len >=readlen) ให้เริ่มต้น //หากความยาวการอ่านบวกกับความยาวที่จะอ่านมากกว่า 10 เท่าของขนาดบล็อก เราจำเป็นต้องเก็บเนื้อหาไว้ที่ส่วนท้ายของการแมปก่อนหน้าเพื่อที่จะรวมเข้ากับเนื้อหาการแมปใหม่ หาก i > 0 จากนั้นเริ่มต้น offset := offset + readlen ; // เขียนไปยังสตรีมชั่วคราว tstream.Write (p ^, readlen-totallen); รายละเอียด จากนั้นแมปความยาวที่เหลือโดยตรงหากขนาดไฟล์ออฟเซ็ต < ขนาดบล็อกแล้ว readlen := png-offset; //Mapping, p เป็นตัวชี้ไปยังพื้นที่การแมป //โปรดทราบว่าพารามิเตอร์ที่สามที่นี่ตั้งค่าเป็น 0 เสมอ ค่านี้ควรตั้งค่าตามสถานการณ์จริง p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)); end; //หากมีข้อมูลในสตรีมชั่วคราว จะต้องรวมเข้าด้วยกันหาก tstream.Size > 0 แล้วเริ่มต้น //คัดลอกข้อมูลสตรีมชั่วคราวผ่าน stream.CopyFrom(tstream,tstream.Size); //จากนั้นเขียนข้อมูลใหม่ในตอนท้ายและรวมเข้าด้วยกันเพื่อให้ stream.Write(p^,len-tstream.Size); - tstream.Size; // ย้ายตำแหน่งของตัวชี้ไปที่จุดเริ่มต้นของ data Inc (p, len-tstream.Size); รวม := รวม + len; Inc(p,len); end; stream.Position := 0; //บันทึกสตรีมลงในไฟล์ stream.SaveToFile(IntToStr(i)+'.txt'); ฟรี ; tstream.Free; CloseHandle(FFile_Handle);
หากคุณมีคำถามใด ๆ โปรดฝากข้อความหรือไปที่ชุมชนของไซต์นี้เพื่อสื่อสารและหารือกัน ขอขอบคุณสำหรับการอ่าน ฉันหวังว่ามันจะช่วยทุกคนได้