读了前5章,感触最深的是作者在例子程序中对UNICODE的考虑,另外就是将windows的API和UNIX进行对比,结合前段时间在看的《UNIX网络编程 卷2》,感觉对比性很强。
印象最深刻的就是第5章的3个排序示例,将外部文件排序用内存来实现,堆,内存映射文件以及基准指针的使用让人大开眼见,不过比之《windows核心编程》还是有差距
#include "EvryThng.h"

#define KEY_SIZE 8


/**//* Structure definition for a tree node. */


typedef struct _TreeNode
{
struct _TreeNode *Left, *Right;
TCHAR Key [KEY_SIZE];
LPTSTR pData;
} TREENODE, *LPTNODE, **LPPTNODE;

#define NODE_SIZE sizeof (TREENODE)
#define NODE_HEAP_ISIZE 0x8000
#define DATA_HEAP_ISIZE 0x8000
#define MAX_DATA_LEN 0x1000
#define TKEY_SIZE KEY_SIZE * sizeof (TCHAR)

LPTNODE FillTree (HANDLE, HANDLE, HANDLE);
BOOL Scan (LPTNODE);

int KeyCompare (LPCTSTR, LPCTSTR); iFile; /**//* for access in exception handler */
BOOL InsertTree (LPPTNODE, LPTNODE);

int _tmain (int argc, LPTSTR argv [])


{
HANDLE hIn, hNode = NULL, hData = NULL;
LPTNODE pRoot;
BOOL NoPrint;
CHAR ErrorMessage[256];
int iFirstFile = Options (argc, argv, _T ("n"), &NoPrint, NULL);

if (argc <= iFirstFile)
ReportError (_T ("Usage: sortBT [options] files"), 1, FALSE);

/**//* Process all files on the command line. */

for (iFile = iFirstFile; iFile < argc; iFile++) __try
{

/**//* Open the input file. */
hIn = CreateFile (argv [iFile], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hIn == INVALID_HANDLE_VALUE)
RaiseException (0, 0, 0, NULL);

/**//* Allocate the two heaps. */

__try
{
hNode = HeapCreate (
HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE, NODE_HEAP_ISIZE, 0);
hData = HeapCreate (
HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE, DATA_HEAP_ISIZE, 0);

/**//* Process the input file, creating the tree. */
pRoot = FillTree (hIn, hNode, hData);

/**//* Display the tree in Key order. */

if (!NoPrint)
{
_tprintf (_T ("Sorted file: %s\n"), argv [iFile]);
Scan (pRoot);
}

} __finally
{ /**//* Heaps and file handle are always closed */

/**//* Destroy the two heaps and data structures. */
if (hNode != NULL) HeapDestroy (hNode);
if (hNode != NULL) HeapDestroy (hData);
hNode = NULL; hData = NULL;
if (hIn != INVALID_HANDLE_VALUE) CloseHandle (hIn);
}

} /**//* End of main file processing loop and try block. */


__except (EXCEPTION_EXECUTE_HANDLER)
{
_stprintf (ErrorMessage, _T("\n%s %s"), _T("sortBT error on file:"), argv[iFile]);
ReportError (ErrorMessage, 0, TRUE);
}
return 0;
}

LPTNODE FillTree (HANDLE hIn, HANDLE hNode, HANDLE hData)


/**//* Scan the input file, creating a binary search tree in the
hNode heap with data pointers to the hData heap. */

/**//* Use the calling program's exception handler. */


{
LPTNODE pRoot = NULL, pNode;
DWORD nRead, i;
BOOL AtCR;
TCHAR DataHold [MAX_DATA_LEN];
LPTSTR pString;

/**//* Open the input file. */

while (TRUE)
{
pNode = HeapAlloc (hNode, HEAP_ZERO_MEMORY, NODE_SIZE);
pNode->pData = NULL;
(pNode->Left) = pNode->Right = NULL;

/**//* Read the key. Return if done. */
if (!ReadFile (hIn, pNode->Key, TKEY_SIZE,
&nRead, NULL) || nRead != TKEY_SIZE)

/**//* Assume end of file on error. */

return pRoot; /**//* Read the data until the end of line. */

AtCR = FALSE; /**//* Last character was not a CR. */


for (i = 0; i < MAX_DATA_LEN; i++)
{
ReadFile (hIn, &DataHold [i], TSIZE, &nRead, NULL);
if (AtCR && DataHold [i] == LF) break;
AtCR = (DataHold [i] == CR);
}
DataHold [i - 1] = '\0';


/**//* DataHold contains the data without the key.
Combine the Key and the Data. */

pString = HeapAlloc (hData, HEAP_ZERO_MEMORY,
(SIZE_T)(KEY_SIZE + _tcslen (DataHold) + 1) * TSIZE);
memcpy (pString, pNode->Key, TKEY_SIZE);
pString [KEY_SIZE] = '\0';
_tcscat (pString, DataHold);
pNode->pData = pString;

/**//* Insert the new node into the search tree. */
InsertTree (&pRoot, pNode);


} /**//* End of while (TRUE) loop */

return NULL; /**//* Failure */
}

BOOL InsertTree (LPPTNODE ppRoot, LPTNODE pNode)


/**//* Insert the new node, pNode, into the binary search tree, pRoot. */


{

if (*ppRoot == NULL)
{
*ppRoot = pNode;
return TRUE;
}
if (KeyCompare (pNode->Key, (*ppRoot)->Key) < 0)
InsertTree (&((*ppRoot)->Left), pNode);
else
InsertTree (&((*ppRoot)->Right), pNode);
return TRUE;
}

int KeyCompare (LPCTSTR pKey1, LPCTSTR pKey2)


/**//* Compare two records of generic characters.
The key position and length are global variables. */


{
return _tcsncmp (pKey1, pKey2, KEY_SIZE);
}

static BOOL Scan (LPTNODE pNode)


/**//* Scan and print the contents of a binary tree. */


{
if (pNode == NULL)
return TRUE;
Scan (pNode->Left);
_tprintf (_T ("%s\n"), pNode->pData);
Scan (pNode->Right);
return TRUE;
}

#include "EvryThng.h"


/**//* Definitions of the record structure in the sort file. */

#define DATALEN 56
#define KEY_SIZE 8


typedef struct _RECORD
{
TCHAR Key [KEY_SIZE];
TCHAR Data [DATALEN];
} RECORD;

#define RECSIZE sizeof (RECORD)
typedef RECORD * LPRECORD;
int KeyCompare (LPCTSTR, LPCTSTR);

int _tmain (int argc, LPTSTR argv [])


{

/**//* The file is the first argument. Sorting is done in place. */

/**//* Sorting is done by file memory mapping. */

HANDLE hFile = INVALID_HANDLE_VALUE, hMap = NULL;
HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
LPVOID pFile = NULL;
DWORD FsLow, Result = 2;
TCHAR TempFile [MAX_PATH];
LPTSTR pTFile;

BOOL NoPrint;
int iFirstFile;

iFirstFile = Options (argc, argv, _T ("n"), &NoPrint, NULL);

if (argc <= iFirstFile)
ReportError (_T ("Usage: sortFL [options] files"), 1, FALSE);


_try
{ /**//* try-except */

/**//* Copy the input file to a temp output file that will be sorted.
Do not alter the input file. */

_stprintf (TempFile, _T ("%s%s"), argv [iFirstFile], _T (".tmp"));

CopyFile (argv [iFirstFile], TempFile, TRUE);


Result = 1; /**//* tmp file is new and should be deleted. */


/**//* Open the file (use the temporary copy). */

hFile = CreateFile (TempFile, GENERIC_READ
| GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

if (hFile ==INVALID_HANDLE_VALUE)
ReportException (_T ("File open failed."), 2);


/**//* Get file size.
If the file is too large, catch that when it is mapped. */

FsLow = GetFileSize (hFile, NULL);


if (FsLow == 0)
{ /**//* There is nothing to sort */
CloseHandle (hFile);

return 0; /**//* You might want to put out an informational message */
}

/**//* Create a file mapping object.
Use the file size but add space for the null character. */

hMap = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, FsLow + TSIZE, NULL);
if (hMap == NULL)
ReportException (_T ("Create File map failed."), 3);


pFile = MapViewOfFile (hMap, FILE_MAP_ALL_ACCESS, 0, 0 /**//* FsLow + TSIZE */, 0);
if (pFile == NULL)
ReportException (_T ("MapView failed"), 4);


/**//* Now sort the file.
Perform the sort with the C library - in mapped memory. */

qsort (pFile, FsLow / RECSIZE, RECSIZE, KeyCompare);


/**//* Print out the entire sorted file. Treat it as one single string. */

pTFile = (LPTSTR) pFile;
pTFile [FsLow/TSIZE] = '\0';
if (!NoPrint)
PrintMsg (hStdOut, pFile);


/**//* Indicate successful completion. */

Result = 0;

ReportException (EMPTY, 5); /**//* Force an exception to clean up. */
return 0;

} /**//* End of inner try-except. */


_except (EXCEPTION_EXECUTE_HANDLER)
{


/**//* Catch any exception here. Indicate any error.
This is the normal termination. Delete the temporary
file and Free all the resources. */

if (pFile != NULL)
UnmapViewOfFile (pFile);
if (hMap != NULL)
CloseHandle (hMap);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle (hFile);
if (Result != 2)
DeleteFile (TempFile);
return Result;
}

} /**//* End of _tmain */


/**//* CODE FROM HERE TO END IS NOT INCLUDED IN TEXT. */

int KeyCompare (LPCTSTR pKey1, LPCTSTR pKey2)


/**//* Compare two records of generic characters.
The key position and length are global variables. */


{
return _tcsncmp (pKey1, pKey2, KEY_SIZE);
}


#include "EvryThng.h"

int KeyCompare (LPCTSTR, LPCTSTR);
DWORD CreateIndexFile (DWORD, LPCTSTR, LPTSTR);

DWORD_PTR KStart, KSize; /**//* Key start position & size (TCHAR). */
BOOL Revrs;

int _tmain (int argc, LPTSTR argv [])


{

/**//* The file is the first argument. Sorting is done in place. */

/**//* Sorting is done by file memory mapping. */


HANDLE hInFile, hInMap; /**//* Input file handles. */

HANDLE hXFile, hXMap; /**//* Index file handles. */
HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
BOOL IdxExists, NoPrint;
DWORD FsIn, FsX, RSize, iKey, nWrite, *pSizes;
LPTSTR pInFile = NULL;
LPBYTE pXFile = NULL, pX;
TCHAR _based (pInFile) *pIn;
TCHAR IdxFlNam [MAX_PATH], ChNewLine = '\n';
int FlIdx;


/**//* Determine the options. */

FlIdx = Options (argc, argv, _T ("rIn"), &Revrs, &IdxExists, &NoPrint, NULL);
if (FlIdx >= argc)
ReportError (_T ("No file name on command line."), 1, FALSE);


/**//* Step 1: Open and Map the Input File. */

hInFile = CreateFile (argv [FlIdx], GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (hInFile == INVALID_HANDLE_VALUE)
ReportError (_T ("Failed to open input file."), 2, TRUE);


/**//* Create a file mapping object. Use the file size. */

hInMap = CreateFileMapping (hInFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hInMap == NULL)
ReportError (_T ("Failed to create input file mapping."), 3, TRUE);
pInFile = MapViewOfFile (hInMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pInFile == NULL)
ReportError (_T ("Failed to map input file."), 4, TRUE);


/**//* Get the file size.
As the mapping succeeded, the file size is less than 2 GB. */

FsIn = GetFileSize (hInFile, NULL);


/**//* Create the index file name. */

_stprintf (IdxFlNam, _T ("%s%s"), argv [FlIdx], _T (".idx"));


/**//* Steps 2 and 3, if necessary. */

if (!IdxExists)
RSize = CreateIndexFile (FsIn, IdxFlNam, pInFile);


/**//* Step 4. Map the index file. */

hXFile = CreateFile (IdxFlNam, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (hXFile == INVALID_HANDLE_VALUE)
ReportError (_T ("Failed to open existing index file."), 5, TRUE);


/**//* Create a file mapping object. Use the file size. */

hXMap = CreateFileMapping (hXFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hXMap == NULL)
ReportError (_T ("Failed to create index file mapping."), 6, TRUE);
pXFile = MapViewOfFile (hXMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pXFile == NULL)
ReportError (_T ("Failed to map index file."), 7, TRUE);
FsX = GetFileSize (hXFile, NULL);


/**//* Get the key size/key start and adjust the file size for the
KeySize/KeyStart fields. Compute the record size from the key size. */

pSizes = (LPDWORD)pXFile; KSize = *pSizes;
KStart = *(pSizes + 1);
FsX -= 2 * sizeof (DWORD);


/**//* Step 5. Sort the index file using qsort. */

if (!IdxExists)
qsort (pXFile + 2 * sizeof (DWORD), FsX / RSize, RSize, KeyCompare);


/**//* Step 6. Output the input file in sorted order. */


/**//* Point to the address of the input file string.
Start in the Input file. */

pX = pXFile + 2 * sizeof (DWORD) + RSize - sizeof (LPTSTR);

if (!NoPrint)

for (iKey = 0; iKey < FsX / RSize; iKey++)
{
WriteFile (hStdOut, &ChNewLine, TSIZE, &nWrite, NULL);


/**//* The cast on pX is important, as it is a pointer to a
byte and we need the four bytes of a based pointer. */
pIn = (TCHAR _based (pInFile)*) *(DWORD_PTR *) pX;

while ((*pIn != CR || *(pIn + 1) != LF) && (SIZE_T) pIn < (SIZE_T)FsIn)
{
WriteFile (hStdOut, pIn, TSIZE, &nWrite, NULL);
pIn++;
}
pX += RSize;
}


/**//* Done. Free all the handles and maps. */

UnmapViewOfFile (pInFile);
CloseHandle (hInMap);
CloseHandle (hInFile);
UnmapViewOfFile (pXFile);
CloseHandle (hXMap);
CloseHandle (hXFile);
return 0;
}
DWORD CreateIndexFile (DWORD FsIn, LPCTSTR IdxFlNam, LPTSTR pInFile)


/**//* Perform Steps 2-3 as defined in program description. */

/**//* This step will be skipped if the options specify use of an existing index file. */


{
HANDLE hXFile;
TCHAR _based (pInFile) *pInScan = 0;
DWORD nWrite;


/**//* Step 2a: Create an index file.
Do not map it yet as its length is not known. */

hXFile = CreateFile (IdxFlNam, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
if (hXFile == INVALID_HANDLE_VALUE)
ReportError (_T ("Failure to create index file."), 8, TRUE);

/**//* Step 2b: Get the first key and determine key size and start. */

KStart = (DWORD_PTR) pInScan;

/**//* Computed start of key field. */
while (*pInScan != ' ' && *pInScan != '\t') pInScan++;

/**//* Computed end of key field */

KSize = ((DWORD_PTR) pInScan - KStart) / TSIZE;


/**//* Computed key field size in characters. */


/**//* Step 2c. Step 3. Scan the complete file, writing keys
and record pointers to the key file. */

/**//* The eight bytes contain the Key Size/Key Start.
This is necessary so that we can re-use the index file. */

WriteFile (hXFile, &KSize, sizeof (DWORD), &nWrite, NULL);
WriteFile (hXFile, &KStart, sizeof (DWORD), &nWrite, NULL);

pInScan = /**//*(TCHAR _based (pInFile)*)*/0;

while ((DWORD_PTR) pInScan < FsIn)
{
WriteFile (hXFile, pInScan + KStart, KSize * TSIZE, &nWrite, NULL);
WriteFile (hXFile, &pInScan, sizeof (LPTSTR), &nWrite, NULL);
while ((DWORD) (DWORD_PTR)pInScan < FsIn && ((*pInScan != CR)

|| (*(pInScan + 1) != LF)))
{

pInScan++; /**//* Skip to end of line. */
}

pInScan += 2; /**//* Skip past CR, LF. */
}
CloseHandle (hXFile);

/**//* Size of an individual record. */
return KSize * TSIZE + sizeof (LPTSTR);
}

int KeyCompare (LPCTSTR pKey1, LPCTSTR pKey2)


/**//* Compare two records of generic characters.
The key position and length are global variables. */


{
DWORD i;
TCHAR t1, t2;
int Result = 0;

for (i = 0; i < KSize && Result == 0; i++)
{
t1 = *pKey1;
t2 = *pKey2;
if (t1 < t2)
Result = -1;
if (t1 > t2)
Result = +1;
pKey1++;
pKey2++;
}
return Revrs ? -Result : Result;
}

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
2007-06-05 解决“显示桌面”快捷方式的丢失