[C ++ source code] folder comparison, file comparison data structure, definition, and implementation

xiaoxiao2021-04-04  237

Description: Originally, I wanted to find a program to synchronize the data folder on my PC and notebook. Of course, the boss should be BEYOND COMPARE in this application, but unfortunately downloaded one, discovery and registration, even the trial is not allowed, so I took a three hours to write one, provide it, if you have a good suggestion, tell me; if you are writing this procedure, you can refer to :), there is what I originally intended to imitate Beyond Compare Make a packaged clistctrl extension class, and later found that I have to do UI and I have to do it. I have to take a lot of effort, so I will be lazy. If the brother is willing to achieve, I can directly implement it on my basics. It's okay. In addition, just finished the Martin Fowler's English version of the English version yesterday, by the way, the theory of the inside is coming :) This is the first time I first put the source code in the 9CBS Forum, I hope if the version Lord feels good, can advocate everyone to provide a little own (except copyright), huh, huh, this next time you search, you can have ready-made code to use ======================================================================================================================================================================================================== ============================================================================================================================================================================================================= ========= Idea: Folder comparison, nothing more than a recursive comparison process: the folder of the same name, recursive comparison; the same name of the file, it is relatively small (I used the bit comparison, it may be slower, but you can pass Set the comparison function address to join your own comparison function); the files of different names are to join. Definition: 4 classes, cfldcmpfilenode, cfldcmpfiletree; cfldcmpnode, cfldcmptree. Two trees are respectively. CfldCMPFileTree: This class is used to analyze the specified folder, and file information, etc. are filled in. Cfldcmpfilenode is the node class of this tree. These two classes are hidden by users. Cfldcmptree: It is a class containing the results of the comparison. This class is open to the user. Detailed Design: The classes of the two trees are used, and the TREE contains information of all root nodes (cfldcmpnode), and then maintains their own child nodes separately. At the same time, each node has a pointer to the parent node. Key Issues: 1. File comparison: The previously said that class cfldcmptree supports custom comparison files (only comparison functions between files), I provide the default comparison function to use bit comparisons (specific implementation is STL Inside the BitSet, another MFC version is directly compared with CString Compare, can use macro _not_use_stl to control comparison functions for compiling STL and MFC versions.

2. Configuration of folder Tree: For ease of comparison, I don't use ID Array format definition trees, and the use of the chain list is to be able to compare recursive comparisons in comparison. The specific method is to recutably the same layer of the same level of the tree. Calling method: Simple code as follows: cfldcmptree tree; tree.parsefolder (szleftpath, szrightpath); file: fldpstruct.hfldcmpstruct.cpp Usage: This, don't have to say it, huh, huh, join your own project.

Header //// File: Fldcmpstruct.h // Written By Alva Chien, 2003.3.24 / / ============================= =================================================== // NOTE: // TO avoid hard code mission, I have change some classes members into // public property, so, you need fix this (ah, a hard work) before you put // this file into a security using.//#ifndef _H_FLDCMP_STRUCT_ # define _H_FLDCMP_STRUCT_ // User bitset to compare file # include #include #include #include using namespace std;! // Include or not # if defined (__ AFXTEMPL_H__) #include #endif //! __ AFXTEMPL_H __ // Using ACLib or not # if! defined (AC_C_COMMON_H _) # define _NOUSE_ACLIB_ # pragma message ( "Complied without using ACLib.dll ...") # endif //! AC_C_COMMON_H_ / /// macro definiton #ifDef _nouse_aclib_ # define FCS_SAFEDEL (P) IF (p! = Null) / {/ delete p; / p = null; /} inline dword fcs_getfilesis (lpctstr lpszfile) { Assert (lpszfile! = Null); if (lpszfile == null) RETUR N 0; #ifdef _not_use_stl dword dwsize = 0; handle hfile; hfile = cretefile (lpszfile, // crete generic_read, // open for writing 0, // do not share null, // no security open_existing, // existing file only File_attribute_normal, // normal file null; // no attr. Template if (hfile! = Invalid_handle_value) {dwsize =

GetFileSize (HFILE, NULL); if (dwsize == invalid_file_size) dwsize = 0; CloseHandle (HFILE); #ELSE IFSTREAM IN (LPSZFILE); Assert (in! = Null); in.seekg (0, iOS :: End); streampos pos = in.tellg (); return (dword) POS; #ENDIF / / _NOT_USE_STL} #ENDIF / / / _NOUSE_AUSE_ALLIB _ / / // Declarations // Folder Compare FunctionTyPedef Int (* Pfnfcmpfunc) (LPARAM, LPARAM , LPARAM); class CFldCmpFileNode; class CFldCmpNode; typedef CFldCmpFileNode * CFldCmpFileNodePtr; typedef CFldCmpNode * CFldCmpNodePtr; typedef CTypedPtrArray CFldCmpFileNodePtrArray; typedef CTypedPtrArray CFldCmpNodePtrArray; // Compare item typetypedef enum {FCT_MATCH, // Match FCT_LNEW, // Left newer FCT_RNEW, // Right newer FCT_LORPHAN, // Left is orphane FCT_RORPHAN // Right is orpane} FldCmpType; //// class CFldCmpFileNodeclass CFldCmpFileNode {// Constructorpublic: CFldCmpFileNode (); virtual ~ CFldCmpFileNode (); / / =================================================================================================================================================================== ============================================== // NOTE: // if You Use the copy constructor or Operator '=' to assign the node, it may // Leads the memory, for it not release the children.// in fact, i strongly suggest use the cfldcmpfiletree instead of build a // tree manual./ / ==== =====================================================================================================================================================

======================================================== cfldcmpfilenode const CFldCmpFileNode & fn); CFldCmpFileNode & operator = (const CFldCmpFileNode & fn); // Operationpublic: // Clean data void cleanData (); // Get family nodes UINT GetFamilyNodeNumber () const; // Memberspublic: // File item CString m_strName; BOOL m_bFolder; COleDateTime m_dtModified; DWORD m_dwSize; // Used to build the tree CFldCmpFileNode * m_pFather; CFldCmpFileNodePtrArray m_arChildPtrs; UINT m_nLevel; // No used DWORD m_dwData;}; //// class CFldCmpFileTreeclass CFldCmpFileTree {// Constructorpublic: CFldCmpFileTree (); virtual ~ CFldCmpFileTree (); // Opertionpublic: // Parse folder BOOL ParseFolder (LPCTSTR lpszPath, CFldCmpFileNodePtr pFa = NULL, UINT nLevel = 0); // Clean data void cleanData (); // Get node number UINT GetNodeNumber () const ; // Get Root Array Const Cfldcmpfilenode () const {return m_arrootptrs;} #ifdef _debug // Display Tree Info Void Display DebugInfo (CFldCmpFileNodePtr pFather = NULL); # endif // _DEBUG // Membersprotected: // Root item array CFldCmpFileNodePtrArray m_arRootPtrs;}; //// class CFldCmpNodeclass CFldCmpNode {// Constructorpublic: CFldCmpNode (); virtual ~ CFldCmpNode (); CFldCmpNode (const cfldcmpnode & cn); cfldcmpnode & operator = (const cfldcmpnode & cn); // Operationpublic: // clean data void cleandata (); // get family nodes uint getFamilyNodenumber () const;

// Compare two node, used for sort int Compare (const CFldCmpNode & nc) const; // Memberspublic: FldCmpType m_type; // Type CString m_strName; // Name COleDateTime m_dtLeft; // Left last modified time COleDateTime m_dtRight; // Right last modified time DWORD m_dwLeftSize; // Left file size DWORD m_dwRightSize; // Right file size BOOL m_bFolder; // Folder or not // Used to build the tree CFldCmpNodePtr m_pFather; // Pointer of father node CFldCmpNodePtrArray m_arChildPtrs; // Children node pointer array UINT m_nLevel; // Level, from the root // No used DWORD m_dwData;}; //// class CFldCmpTreeclass CFldCmpTree {// Constrcutionpublic: CFldCmpTree (); virtual ~ CFldCmpTree (); // Interfacepublic: // Clean data void cleanData (); # ifdef _DEBUG // Display tree info void DisplayDebugInfo (CFldCmpNodePtr pFather = NULL); # endif // _DEBUG // Get node number UINT GetNodeNumber () const; // Combine file tree BOOL ParseFolder (LPCTSTR lpszLeft, LPCTSTR LPSZRI ght); // Get root array const CFldCmpNodePtrArray & GetRootArray () const {return m_arRootPtrs;} // Sort void Sort (); // Compare function void SetCustomSortFunc (PFNFCCMPFUNC); // Get node full path CString GetNodeFullPath (const CFldCmpNodePtr pNode, BOOL bLeft = TRUE) const; // Operationsprotected: // Combine file tree BOOL CombineFileTrees (const CFldCmpFileTree & treeLeft, const CFldCmpFileTree & treeRight); // Copy left file tree BOOL CopyLeftFileTree (const CFldCmpFileNodePtrArray & arNodePtrs, CFldCmpNodePtr pFather = NULL); // Combine Right File Tree Bool Combinerighttree (Const CfldcmpfilenodeptraftTRAY & ArfilenodePTRS, Cfldcmpnodeptr Pfather =

NULL); // Sort function void SortArray (CFldCmpNodePtrArray & arArray); // Membersprotected: // Root item array CFldCmpNodePtrArray m_arRootPtrs; // File's compare function PFNFCCMPFUNC m_pFnFileCmp; // Left path CString m_strLeftPath; // Right path CString m_strRightPath;}; #ENDIF / / _H_FLDCMP_STRUCT_

Implement the top half of the file: //// File: fldcmpstruct.cpp // Written by Alva Chien, 2004.3.24 // # include "stdafx.h" #include "fdcmpstruct.h" //// default file compare function / / Support a default compare functionint FCS_DefaultFileCmp (LPARAM lpNode, LPARAM lpszLeftFile, LPARAM lpszRightFile) {ASSERT ((lpNode = NULL) && (lpszLeftFile = NULL) && (lpszRightFile = NULL)!!!); if ((lpNode == NULL) || (lpszLeftFile == NULL) || (lpszRightFile == NULL)) return FALSE; CFldCmpNodePtr pNode = (CFldCmpNodePtr) lpNode;? if (pNode-> m_bFolder) // Folder return 0; DWORD dwSizeLeft = pNode-> m_dwLeftSize, dwSizeRight = pNode-> m_dwRightSize; int nRst = 0; BOOL bContinue = FALSE;! if (dwSizeLeft == dwSizeRight) {#if defined (_NOT_USE_STL) // Read the file content ifstream inLeft ((LPCTSTR) lpszLeftFile); ifstream inRight ( (LPCTSTSTSTSTSTSTSTSTFILE); Assert! = Null) && (INRIGHT! = NULL)); strstream strright, strright; strright << inleft.rdbuf (); strright << INRIGHT.RDBUF (); inleft.close ); INRIG Ht.close (); // in 32 bits computers, sizeof (byte) == 8, so 256 * 8 => 2048 char * PLEFT = strright.str (); char * pRight = strright.str (); int NTIME = DWSIZELEFT / 256; IF (DWSIZELEFT% 256) NTIME = 1; // Hard Work Again, 55555555 .... for (INT i = 0; I BSLEFT (PLEFT I * 2048, 2048); BitSet <2048> BSRight (PRIGHT i * 2048, 2048); int ncount = BSLEFT.COUNT ();

BSLEFT & = BSRight; IF (ncount! = bsleft ()) {// not Same! Out! bcontinue = true; break;}} else {bitset <2048> BSLEFT (PLEFT I * 2048); bitset <2048 > BSRIGHT (PRIGHT I * 2048); int ncount = bsleft.count (); bsleft & = bsright; if (ncount! = Bsleft.count ()) {// NOT SAME! OUT! BCONTINUE = True; Break;} }} Delete strright.str (); delete strright.str (); # else lpbyte pleft = new byte [dwsizeleft]; lpbyte pryt = new byte [dwsizeleft]; assert (Pleft! = Null) && (PLEFT! = Null) && (PRIGHT! = Null )); CFile fileLeft, fileRight; if (fileLeft.Open ((LPCTSTR) lpszLeftFile, CFile :: modeRead)) {fileLeft.ReadHuge (pLeft, dwSizeLeft); fileLeft.Close ();} if (fileRight.Open ((LPCTSTR LpszrightFile, cfile :: modeRead) {Fileright.Readhuge (PRIGHT, DWSIZELEFT); Fileright.close ();} if (cstract) == 0) bcontinue = false; delete [ ] PLE ft; pleft = null; delete [] PRIGHT; PRight = null; #ENDIF / /! _NOT_USE_STL if (! bcontinue) return 0;} // no same, compare it by the time if (pnode-> m_dtleft> pnode-> m_dtRight) nRst = 1; else if (pNode-> m_dtLeft m_dtRight) nRst = -1; else nRst = 0; return nRst;} //// class CFldCmpFileNode // ConstructorCFldCmpFileNode :: CFldCmpFileNode () {m_strName = _T (""); m_bfolder = false; m_dtmodified = coledatetime :: getcurrenttime (); m_dwsize = 0; // buy to build the tree m_pfather = null; m_archildptrs.setsize (0); m_nlevel = (uint) -1;

// Used for arrange m_dwData = 0;} // DecomstructorCFldCmpFileNode :: ~ CFldCmpFileNode () {CleanData ();} // Copy constructorCFldCmpFileNode :: CFldCmpFileNode (const CFldCmpFileNode & fn) {* this = fn;} // Override operator = CFldCmpFileNode & CFldCmpFileNode :: operator = (const CFldCmpFileNode & fn) {if (this = & fn!) {m_strName = fn.m_strName; m_bFolder = fn.m_bFolder; m_dtModified = fn.m_dtModified; m_dwSize = fn.m_dwSize; // Used to build the tree m_pFather = fn.m_pFather; m_arChildPtrs.RemoveAll (); m_arChildPtrs.Copy (fn.m_arChildPtrs); m_nLevel = fn.m_nLevel; // Used for arrange m_dwData = fn.m_dwData;} return * this;} // Clean datavoid CFldCmpFileNode: : Cleandata () {for (int i = 0; i cleandata (); # ifdef _Nouse_aclib_ fcs_safedel (pnode); # else ac_safedel (pnode); # endif // _nouse_aclib_} m_archildptrs.removeall ();} // Get Node family numberUINT CFldCmpFileNode :: GetFamilyNodeNumber () const {UINT nCount = 0; for (int i = 0; i GetFamilyNodeNumber (); nCount = 1; // Himself return nCount;} //// class CFldCmpFileTree // ConstructorCFldCmpFileTree :: CFldCmpFileTree () {m_arRootPtrs.SetSize (0);} // DecomstructorCFldCmpFileTree :: ~ CFldCmpFileTree () {cleanData ();} / / Get node number :: getnodenumber () const {uint ncount = 0; for (int i = 0; i

GetFamilyNodeNumber (); return nCount;} // Parse folderBOOL CFldCmpFileTree :: ParseFolder (LPCTSTR lpszPath, CFldCmpFileNodePtr pFa, UINT nLevel) {ASSERT (lpszPath = NULL!); If (lpszPath == NULL) return FALSE; CString str = lpszPath; IF (str.Isempty ()) Return False; str.trimright (); str.trimright (_t ('//')); str = _t ("//*.*"); // Start Working for Files CFileFind finder; BOOL bWorking = finder.FindFile (str); while (bWorking) {bWorking = finder.FindNextFile (); if (finder.IsDots ()) continue; WIN32_FILE_ATTRIBUTE_DATA fad; CFldCmpFileNodePtr pNode = new CFldCmpFileNode; ASSERT (pNode =! NULL); if (pnode == null) Return false; pnode-> m_strname = finder.getFileName (); pnode-> m_nlevel = NLVEL; pnode-> m_pfather = pfa; if (Finder.Indirectory ()) {PNODE-> M_bfolder = true; // add it t ARRAY IF (PFA == Null) m_arrootptrs.add (pnode); Else Pfa-> m_archildPTRS.Add (PNode); // Recursiving ... if return FALSE (ParseFolder (finder.GetFilePath (), pNode, nLevel 1)!);} Else {pNode-> m_bFolder = FALSE; if (GetFileAttributesEx (finder.GetFilePath (), GetFileExInfoStandard, & fad)! = FALSE) {pNode-> m_dtModified = (COleDateTime) fad.ftLastWriteTime; #ifdef _NOUSE_ACLIB_ pNode-> m_dwSize = FCS_GetFileSize (finder.GetFilePath ()); # else pNode-> m_dwSize = ACC_GetFileSize (finder.GetFilePath ()); # ENDIF / / _NOUSE_ACLIB_} // Add IT IF (PFA ==

NULL) m_arrootptrs.add (pnode); Else Pfa-> m_archildptrs.add (pnode);}} Return True;} // clean datavoid cfldcmpfiletree :: cleandata () {for (int i = 0; i cleanData (); # ifdef _NOUSE_ACLIB_ FCS_SAFEDEL (pNode); # else ACC_SAFEDEL (pNode); # endif // _NOUSE_ACLIB_ ?} m_arRootPtrs.RemoveAll ();} # ifdef _DEBUG // Display debug infovoid CFldCmpFileTree :: DisplayDebugInfo (CFldCmpFileNodePtr pFather) {CFldCmpFileNodePtrArray & rNods = (pFather == NULL) m_arRootPtrs: pFather-> m_arChildPtrs; for (int i = 0; i m_nlevel, pfather-> m_strname, pfather-> m_bfolder? _t (" folder "): _t (" file "), (pfather-> m_pfather == NULL)? _T ("none": pfather-> m_pfathe R-> m_strname, pfather-> m_dwsize, pfather-> m_archildptrs.getsize ());}} # endif // _debug

Implementation files intermediate portion //// class CFldCmpNode // ConstructorCFldCmpNode :: CFldCmpNode () {m_type = FCT_MATCH; // Type m_strName = _T ( ""); // Name m_dtLeft = COleDateTime :: GetCurrentTime (); // Left last modified time m_dtRight = COleDateTime :: GetCurrentTime (); // Right last modified time m_dwLeftSize = 0; // Left file size m_dwRightSize = 0; // Right file size m_bFolder = FALSE; // Folder or not // Used to build the Tree m_pfather = null; // Pointer of Father node m_archildptrs.setsize (0); // children node pointer array m_nlevel = (uint) -1; // Level, from the root // used data m_dwdata = 0;} cfldcmpnode: : ~ CFldCmpNode () {cleanData ();} CFldCmpNode :: CFldCmpNode (const CFldCmpNode & cn) {* this = cn;} CFldCmpNode & CFldCmpNode :: operator = (const CFldCmpNode & cn) {if (! this = & cn) {m_type = cn. m_type; // type m_strname = cn.m_strname; // name m_dtleft = cn.m_dtleft; // Left last modified time m_dtright = cn.m_dtright; // Right Last Modified Time M_DWLEF tSize = cn.m_dwLeftSize; // Left file size m_dwRightSize = cn.m_dwRightSize; // Right file size m_bFolder = cn.m_bFolder; // Folder or not // Used to build the tree m_pFather = cn.m_pFather; // Pointer of father node m_arChildPtrs.RemoveAll (); m_arChildPtrs.Copy (cn.m_arChildPtrs); m_nLevel = cn.m_nLevel; // Level, from the root // Used for arrange m_dwData = cn.m_dwData;} return * this;} // Clean Datavoid cfldcmpnode :: cleandata () {for (int i = 0; i

pNode-> CleanData (); # ifdef _NOUSE_ACLIB_ FCS_SAFEDEL (pNode); # else ACC_SAFEDEL (pNode); # endif // _NOUSE_ACLIB_} m_arChildPtrs.RemoveAll ();} // Get family nodesUINT CFldCmpNode :: GetFamilyNodeNumber () const {UINT nCount = 0; for (int i = 0; i getFamilyNodenumber (); ncount = 1; // himself retturn ncount;} / / Compareint cfldcmpnode :: Compare (const cfldcmpnode & nc) const {int NRST = 0; if (m_bfolder && (! Nc.m_bfolder)) NRST = -1; ELSE IF ((! M_bfolder) && (nc.m_bfolder)) NRST = 1; else {nRst = lstrcmpi (m_strName, nc.m_strName);} return nRst;} //// class CFldCmpTree // ConstructorCFldCmpTree :: CFldCmpTree () {m_arRootPtrs.SetSize (0); m_pFnFileCmp = FCS_DefaultFileCmp;} // DeconstructorCFldCmpTree :: ~ cfldcmptree () {cleandata ();} // clean datavoid cfldcmptree :: cleandata () {for (int i = 0; i CLEA nData (); # ifdef _NOUSE_ACLIB_ FCS_SAFEDEL (pNode); # else ACC_SAFEDEL (pNode); # endif // _NOUSE_ACLIB_} m_arRootPtrs.RemoveAll ();} # ifdef _DEBUG // Display tree infovoid CFldCmpTree :: DisplayDebugInfo (CFldCmpNodePtr pFather) {CFldCmpNodePtrArray & rnods = (pfather == null) M_arrootptrs: pfather-> m_archildptrs; // then clean itselft if ("Tree Level% D:% S,% S, Father% s, Left size% D, Right Size% D, Children Number% D / N ", Pfather-> M_NLVEL, PFATHER-> M_STRNAME, PFATHER-> M_BFOLDER? _T (" folder "): _t (" file "), (pfather->

m_pfather == null)? _T ("none): pfather-> m_pfather-> m_strname, pfather-> m_dwleftsize, pfather-> m_dwrightsize, pfather-> m_archildptrs.getsize ());} for (int i = 0; i m_strName = pFile-> m_strName; pNode-> m_bFolder = pFile-> m_bFolder; pNode-> m_dtLeft = pFile-> m_dtModified; pNode-> m_dwLeftSize = Pfile-> m_dwsize; pnode-> m_nlevel = pfile-> m_nlevel; pnode-> m_pfather = pfather; pnode-> m_type = fct_lorphan; if (pfather! = Null) Pfather-> m_archildptrs.add (pnode ); Else m_arRootPtrs.Add (pNode); // Add it's children CopyLeftFileTree (pFile-> m_arChildPtrs, pNode);} return TRUE;} // Get node numberUINT CFldCmpTree :: GetNodeNumber () const {UINT nCount = 0; for ( INT i = 0; i getFamilyNodenumber (); return ncount;}

// Combine the right treeBOOL CFldCmpTree :: CombineRightTree (const CFldCmpFileNodePtrArray & arFileNodePtrs, CFldCmpNodePtr pFather) {CFldCmpNodePtrArray & arNodes = (pFather == NULL) m_arRootPtrs:? PFather-> m_arChildPtrs; BOOL bExist = FALSE; int i = 0; int nArSize = arNodes .GetSize (); for (; i m_strname == pfilenode-> m_strname) {bexist = true; break;}}} (bexist) {// set the info pnode = arNodes.ElementAt (j); pNode-> m_dtRight = pFileNode-> m_dtModified; pNode-> m_dwRightSize = pFileNode-> m_dwSize; CString strLeft = GetNodeFullPath (pNode); CString strRight = GetNodeFullPath (pNode, FALSE); ASSERT (m_pFnFileCmp! = NULL); int NRST = (m_pfnfileCMP) ((lparam) PNODE , (Lpctstr) strright; if (nRST == 0) pnode-> m_type = fct_match; else if (nrst> 0) pnode-> m_type = fct_lnew; else pnode-> m_type = FCT_RNEW;} else {pNode = new CFldCmpNode; ASSERT (pNode = NULL!); pNode-> m_type = FCT_RORPHAN; pNode-> m_dtRight = pFileNode-> m_dtModified; pNode-> m_dwRightSize = pFileNode-> m_dwSize; pNode-> m_nlevel = pfilenode-> m_nlevel; pnode-> m_strname = pfilenode-> m_strname; PNODE->

m_bFolder = pFileNode-> m_bFolder; pNode-> m_pFather = pFather; arNodes.Add (pNode);} // The children pointers CombineRightTree (pFileNode-> m_arChildPtrs, pNode);} return TRUE;} // Combine the file treeBOOL CFldCmpTree: : CombineFileTrees (const CFldCmpFileTree & treeLeft, const CFldCmpFileTree & treeRight) {cleanData (); // First: Copy the left CopyLeftFileTree (treeLeft.GetRootArray ()); // Trace info TRACE0 ( "/ nAfter copy left file tree ... == =========================================== / n "); Trace (" / TTotal Number IS% D ... ======================= / n / n ", getnodenumber ()); DisplayDebugInfo (); trace0 (" / n "); // second: Combine The right Tree Combinerighttree ()); // trace info trace0 ("/ NAFTER Combine right file tree ... ====================== ============= / n "); Trace (" / Ttotal Number IS% D ... ==================== == / N / N ", getnodenumber ()); DisplayDebuginfo (); trace0 (" / n "); return true;} / sort the arrayvoid cfldcmptree :: SortArray (Cfldcmpnodeptra Rray & Ararray) {Int nsize = Ararray.getsize (); INT i = 0; // sort it for (; i

转载请注明原文地址:https://www.9cbs.com/read-131771.html

New Post(0)