// FileChopper.h #ifndef FileChopperh #define FileChopperh #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "CSV.h" #include "Array.h" #include "CSV.h" /* Uses Binary Chop Search to find a text record in a file using the first field as a Key. Create a text file with the following eight lines of text in: "FIFTH", FIVE "FIRST" , ONE FOURTH, FOUR NINTH, NINE SECOND, TWO SEVENTH, SEVEN SIXTH, SIX TENTH, TEN THIRD, THREE The following code will read the file, searching for the line starting with "FIRST", then print the next Field in the debug window: CFileChopper Chop("t.txt",20); // 20 Characters including \r\n TRACE("\r\n"); if(Chop.Find("FIRST" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("SECOND" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("THIRD" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("FOURTH" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("FIFTH" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("SIXTH" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("SEVENTH")) TRACE(Chop.GetNextField()+", "); if(Chop.Find("EIGHTH" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("NINTH" )) TRACE(Chop.GetNextField()+", "); if(Chop.Find("TENTH" )) TRACE(Chop.GetNextField()+"\r\n"); TRACE("\r\n"); This is the output in the debug window: ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN */ class CFileChopper : public CCSV { HANDLE hFile; DWORD MaxRecordLength; CFileChopper(); // No default constructor CFileChopper(const CFileChopper& FileChopper); // No Copying DWORD Seek(DWORD Pos, bool FromEnd=false) { LARGE_INTEGER LPos={0}; LPos.LowPart=Pos; return SetFilePointerEx(hFile, LPos, &LPos, FromEnd ? FILE_END : FILE_BEGIN) ? LPos.LowPart : 0; } public: CFileChopper(const CString& Path, DWORD MaxRecordLength=1024) : MaxRecordLength(MaxRecordLength) {try{hFile=CreateFile(Path, GENERIC_READ, 0,0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);}catch(...){ASSERT(0);}} virtual ~CFileChopper() {try{CloseHandle(hFile);}catch(...){ASSERT(0);}} bool Find(const CString& Target) { int i=-1; try{ if(hFile==INVALID_HANDLE_VALUE) return false; DWORD stt=0; DWORD end=Seek(0,true); while(stt!=end) { DWORD mid=(stt+end)/2; // Binary Chop algorithm: i=Compare(mid,Target); if(i==0) return true; if(i>0) end=mid; else stt=mid+1; } }catch(...){ASSERT(0);} return i==0; } protected: bool isNL(char c) const {switch(c) {case '\r': case '\n': case 0: return true; default: return false;}} /* Just choosing the middle of the file won't put us at the middle record. We will probably end up pointing at the middle of a record too. We want to find the beginning of the record that we're pointing to, so we read a previous record as well (if it exists). */ int Compare(DWORD Pos, const char* Target) { ::Array Buffer(2*(MaxRecordLength+1)); // read two records, the middle of the buffer is where we start looking from. *Buffer.Data=0; // make a leading null as a terminator for our reverse search. Seek((Pos>MaxRecordLength) ? Pos-MaxRecordLength : 0); // Rewind a max record length (if possible). DWORD iBytes=0; if(!ReadFile(hFile, Buffer.Data+1, 2*MaxRecordLength, &iBytes, 0) // -2 = the two null terminators (one either end) || (iBytes