Details to do the search

Hi,
In addition to this thread
http://www.experts-exchange.com/Programming/Languages/CPP/Q_28616998.html#a40644825

should I use fouritems to search either flname or flnumber big files, per given name or number?
LVL 12
HuaMin ChenProblem resolverAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

sarabandeCommented:
here are the relevant structures:

struct index_name
{
    unsigned int recnum;
    char name[21];
    bool operator< (const index_name & ni) const
    {
        if (strcmp(name, ni.name) < 0) return true;
        return false;
    } 
};

struct index_number
{
    unsigned int recnum;
    long long number;
    bool operator< (const index_number & ni) const
    {
        if (number < ni.number) return true;
        return false;
    }
};

struct fouritems
{
    item items[4];
    fouritems(bool bfill = false); 

};

Open in new window


I would suggest your program to make a binary search for numbers or names either gets the search criteria from command line or would ask for it at the command prompt:

int main(int nargs, char szargs[])
{
       std::string strsearch;
       bool bquit = false;

       while (bquit == false)
       {      
            if (nargs == 1)
            {
                   std::cout << "Enter your Search Criteria: (CTRL-Z to stop) " << std::endl;
                   if (!std::getline(std::cin, strsearch) || strsearch == "q" || strsearch == "Q")
                   {
                        bquit = true;
                        continue;
                   }
             }
             // if a command was given we expect the search criteria to be passed as 1st argument
             else 
             {
                    strsearch = szargs[1];
                    // we would leave the loop after query
                    bquit = true;   
              }
             // in both cases we parse the search criteria and if ok we perform it.
             std::string strname;
             long long number;
             bool bsearchForName = false;
             bool bvalid = parseSearchCriteria(strname, number, bsearchForName); 
             if (bvalid == false)
             {
                   std::cout << std::endl << "[" << strsearch << "], " 
                   << "wrong search criterium. Required syntax: 'Name=<name>' or 'Number=<number>'." 
                   << std::endl << std::endl;
                  continue;     // if nargs == 1 a new query can be entered.
              }
              std::string strresult;
              bool bfound = performBinarySearch(strname, number, bsearchForName, strresult);
              displayResult(bfound, strsearch, strresult);
       }
       return 0;
}

Open in new window



Sara
HuaMin ChenProblem resolverAuthor Commented:
Thanks a lot.
Sara,
Can I know if we should use "parseSearchCriteria" to decide whether the input part is either a string or a number?
sarabandeCommented:
could be like:

bool parseSearchCriteria(const std::string & strsearch, 
                         std::string & strname, long long & number, bool & bsearchForName)
{
    bool bvalid = false;
    // looking for NAME= or NUMBER=
    size_t pos = strsearch.find('=');
    if (pos != std::string::npos)
    {
        std::string strcrit(pos, '\0');
        std::transform(strsearch.begin(), strsearch.begin()+pos, strcrit.begin(), toupper);
        if (strcrit.find("NAME") == 0)
        {
            if ((pos = strsearch.find_first_not_of(" ", pos+1)) != std::string::npos)
            {
                strname = strsearch.substr(pos);
                bvalid = bsearchForName = true;
            }
        }
        else if (strcrit.find("NUMBER") == 0)
        {
            std::istringstream isn(strsearch.substr(pos+1));
            if (isn >> number)
            {
                bvalid = true;
                bsearchForName = false;
            }
        }
    }
    return bvalid;
} 

Open in new window

     

note, i added the search criterium as input argument.              

Sara
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

HuaMin ChenProblem resolverAuthor Commented:
Many thanks Sara.
How to correct his?
bool performBinarySearch(std::string & strname, long long & number, bool & bsearchForName, std::string & strresult)
{
	long nbegin = 0;
	long nend = numRecords - 1;
	long nmid;
	char nm_got[100];
	unsigned int val_got;
	unsigned int nstop = 0;
	std::ifstream nameindexfile;
	std::ifstream numberindexfile;
	index_name idxnam = { 0 };
	index_number idxnum = { 0 };

	if (bsearchForName = true)
	{
		std::string strnameindexfile = createFilename("c:\\dp4\\flname", ".idx");
		nameindexfile.open(strnameindexfile.c_str(), std::ios::binary | std::ios::in);
		if (!nameindexfile.is_open())
			return -3; //
		if (!nameindexfile.read((char*)&idxnum, sizeof(index_name)))
			return -4; //
		ret = _stat64(strnameindexfile.c_str(), &fs64);
		if (ret != 0)
		{
			return -5;
		}
		numRecords = (int)(fs64.st_size / sizeof(index_name)); // make numRecords a size_t 

		char szArgv2[512] = { 0 };
		size_t ncharsConverted = 0;
		wcstombs(szArgv2, strname, sizeof(szArgv2));
		while (nbegin <= nend && nstop != -1)
		{
			nmid = (nbegin + nend) / 2;
			index_name rec = { 0 };
			nameindexfile.seekg(nmid* sizeof(index_name));
			nameindexfile.read((char*)&rec, sizeof(index_name));
			if (strcmp(szArgv2, rec.name)<0)
			{
				nend = nmid - 1;
				nmid = (nbegin + nend) / 2;
			}
			else
			{
				if (strcmp(szArgv2, rec.name)>0)
				{
					nbegin = nmid + 1;
					nmid = (nbegin + nend) / 2;
				}
				else
				{
					nstop = -1;
					strcpy(strresult, rec.name);
					return true;
				}
			}
		}
	}
	else
	{
		std::string strnumberindexfile = createFilename("c:\\dp4\\flnumber", ".idx");
		numberindexfile.open(strnumberindexfile.c_str(), std::ios::binary | std::ios::in);
		if (!numberindexfile.is_open())
			return -3; //
		if (!numberindexfile.read((char*)&idxnum, sizeof(index_number)))
			return -4; //
		ret = _stat64(strnumberindexfile.c_str(), &fs64);
		if (ret != 0)
		{
			return -5;
		}
		numRecords = (int)(fs64.st_size / sizeof(index_number)); // make numRecords a size_t 

		char szArgv2[512] = { 0 };
		size_t ncharsConverted = 0;
		wcstombs(szArgv2, strname, sizeof(szArgv2));
		while (nbegin <= nend && nstop != -1)
		{
			nmid = (nbegin + nend) / 2;
			index_number rec = { 0 };
			numberindexfile.seekg(nmid* sizeof(index_number));
			numberindexfile.read((char*)&rec, sizeof(index_number));
			if (strcmp(szArgv2, rec.number)<0)
			{
				nend = nmid - 1;
				nmid = (nbegin + nend) / 2;
			}
			else
			{
				if (strcmp(szArgv2, rec.number)>0)
				{
					nbegin = nmid + 1;
					nmid = (nbegin + nend) / 2;
				}
				else
				{
					nstop = -1;
					//strcpy(nm_got, rec.name);
					val_got = rec.number;
					std::stringstream ss;
					ss << val_got;
					strcpy(strresult, ss.str());
					return true;
				}
			}
		}
	}

	return false;
}

Open in new window

HuaMin ChenProblem resolverAuthor Commented:
Any help to this? Thanks a lot and please take your time.
HuaMin ChenProblem resolverAuthor Commented:
Take care Sara, as I'm not pushing you actually.
sarabandeCommented:
you may take these two functions:

template <class index> 
bool binarySearch(const std::string & strindexprefix, const index & searchindex, std::string & strresult, index & foundindex)
{
    std::string strindexfile = createFilename(strindexprefix, ".idx");
    std::ifstream indexfile(strindexfile.c_str(), std::ios::binary | std::ios::in);
	if (!indexfile.is_open())
	{
        strresult = "open error";
		return false;
	}
    index idx = { 0 };
	if (!indexfile.read((char*)&idx, sizeof(index)))
	{
        strresult = "read error";
		return false;
	}
    struct _stat64 fs64 = { 0 };
    int ret = _stat64(strindexfile.c_str(), &fs64);
	if (ret != 0)
	{
        strresult = "stat64 error";
		return false;
	}
	size_t numRecords = (int)(fs64.st_size / sizeof(index));  

    long long nbegin = 0;
    long long nend   = numRecords - 1;
    long long nmid   = 0;
	while (nbegin <= nend)
	{
		nmid = (nbegin + nend) / 2;
		index indexrecord = { 0 };
		indexfile.seekg(nmid * sizeof(index));
		if (!indexfile.read((char*)&indexrecord, sizeof(index_name)))
        {
            strresult = "read error index record";
        }
        if (searchindex < indexrecord)
		{
			nend = nmid - 1;
			nmid = (nbegin + nend) / 2;
		}
		else
		{
			if (indexrecord < searchindex)
			{
				nbegin = nmid + 1;
				nmid = (nbegin + nend) / 2;
			}
			else
			{
				strresult  = indexrecord.getresult();
                foundindex = indexrecord;
				return true;
			}
		}
	}

    return false;
}

bool performBinarySearch(const std::string & strname, long long number, bool & bsearchForName, std::string & strresult)
{
    bool bfound = false;
    unsigned int recnum = 0;
    if (bsearchForName)
    {
        index_name  searchindex = { 0 };
        index_name  foundindex  = { 0 };

        strcpy_s(searchindex.name, sizeof(searchindex.name)-1, strname.c_str());
        bfound = binarySearch<index_name>("c:\\dp4\\flname", searchindex, strresult, foundindex);
        recnum = foundindex.recnum;
    }
    else
    {
        index_number  searchindex = { 0 };
        index_number  foundindex  = { 0 };
        searchindex.number = number;
        bool bfound = binarySearch<index_number>("c:\\dp4\\flnumber", searchindex, strresult, foundindex);
        recnum = foundindex.recnum;
    }
    // still open: if bfound is true we have to read recnum from data file 
    // use a function getresult to get a string result from fouritems
    // then you could add this to the strresult returned from binarySearch
    return bfound;
}

Open in new window


Sara
sarabandeCommented:
here are the index structures where i added member function getresult.

struct index_name
{
    unsigned int recnum;
    char name[21];
    bool operator< (const index_name & ni) const
    {
        if (strcmp(name, ni.name) < 0) return true;
        return false;
    } 
    std::string getresult() const { return name; }
};

struct index_number
{
    unsigned int recnum;
    long long number;
    bool operator< (const index_number & ni) const
    {
        if (number < ni.number) return true;
        return false;
    }
    std::string getresult() const { std::ostringstream oss; return ((std::ostringstream & )(oss << number)).str();  }
};

Open in new window


Sara
HuaMin ChenProblem resolverAuthor Commented:
Many many thanks Sara.
How to resolve these
Error	4	error C2664: 'size_t wcstombs(char *,const wchar_t *,size_t)' : cannot convert argument 2 from 'std::string' to 'const wchar_t *'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	144	1	ReadBinaryFile
Error	5	error C2664: 'char *strcpy(char *,const char *)' : cannot convert argument 1 from 'std::string' to 'char *'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	166	1	ReadBinaryFile
Warning	6	warning C4305: 'return' : truncation from 'int' to 'bool'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	177	1	ReadBinaryFile
Warning	7	warning C4305: 'return' : truncation from 'int' to 'bool'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	179	1	ReadBinaryFile
Warning	8	warning C4305: 'return' : truncation from 'int' to 'bool'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	183	1	ReadBinaryFile
Error	9	error C2664: 'size_t wcstombs(char *,const wchar_t *,size_t)' : cannot convert argument 2 from 'std::string' to 'const wchar_t *'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	189	1	ReadBinaryFile
Error	10	error C2664: 'int strcmp(const char *,const char *)' : cannot convert argument 2 from '__int64' to 'const char *'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	196	1	ReadBinaryFile
Error	11	error C2664: 'int strcmp(const char *,const char *)' : cannot convert argument 2 from '__int64' to 'const char *'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	203	1	ReadBinaryFile
Error	12	error C2664: 'char *strcpy(char *,const char *)' : cannot convert argument 1 from 'std::string' to 'char *'	Z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	212	1	ReadBinaryFile
	13	IntelliSense: no suitable conversion function from "std::string" to "const wchar_t *" exists	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	144	21	ReadBinaryFile
	14	IntelliSense: no suitable conversion function from "std::string" to "char *" exists	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	166	13	ReadBinaryFile
	15	IntelliSense: no suitable conversion function from "std::string" to "const char *" exists	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	166	24	ReadBinaryFile
	16	IntelliSense: no suitable conversion function from "std::string" to "const wchar_t *" exists	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	189	21	ReadBinaryFile
	17	IntelliSense: argument of type "long long" is incompatible with parameter of type "const char *"	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	196	24	ReadBinaryFile
	18	IntelliSense: argument of type "long long" is incompatible with parameter of type "const char *"	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	203	25	ReadBinaryFile
	19	IntelliSense: no suitable conversion function from "std::string" to "char *" exists	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	212	13	ReadBinaryFile
	20	IntelliSense: no suitable conversion function from "std::string" to "const char *" exists	z:\cmp3\ReadBinaryFile\ReadBinaryFile\ReadBinaryFile.cpp	212	24	ReadBinaryFile

Open in new window


to these codes?
// 
//

#pragma warning (disable: 4996) 
#include "stdafx.h"
#include <set>
#include <sys/stat.h>
#include <string>
#include <fstream>
#include <sstream>
#include <atlbase.h>
#include <ctype.h>
#include <process.h>
#include <vector>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <Windows.h>
#include "..\..\include\struc4.h"

const unsigned int NUM_FILES = 80;
const unsigned int NUM_RECORDS = 1000000;
struct _stat64 fs64 = { 0 };
int ret; 
long numRecords;

struct index_name
{
	unsigned int recnum;
	char name[21];
	bool operator< (const index_name & ni) const
	{
		if (strcmp(name, ni.name) < 0) return true;
		return false;
	}
	std::string getresult() const { return name; }
};

struct index_number
{
	unsigned int recnum;
	long long number;
	bool operator< (const index_number & ni) const
	{
		if (number < ni.number) return true;
		return false;
	}
	std::string getresult() const { std::ostringstream oss; return ((std::ostringstream &)(oss << number)).str(); }
};

struct item
{
	char name[21];
	long long number;
	char description[100];
	bool operator<(const item & it) const
	{
		return (strcmp(name, it.name) < 0);
	}
};

struct fouritems
{
	item items[4];
	fouritems(bool bfill = false);

};

bool LessComp(const struc4& a1, const struc4& a2)
{
	if (strcmp(a1.item1, a2.item1) < 0) return true;
	if (strcmp(a1.item1, a2.item1) > 0) return false;
	if (a1.i1_int < a2.i1_int) return true;
	return false;
}
bool parseSearchCriteria(const std::string & strsearch,
	std::string & strname, long long & number, bool & bsearchForName)
{
	bool bvalid = false;
	// looking for NAME= or NUMBER=
	size_t pos = strsearch.find('=');
	if (pos != std::string::npos)
	{
		std::string strcrit(pos, '\0');
		std::transform(strsearch.begin(), strsearch.begin() + pos, strcrit.begin(), toupper);
		if (strcrit.find("NAME") == 0)
		{
			if ((pos = strsearch.find_first_not_of(" ", pos + 1)) != std::string::npos)
			{
				strname = strsearch.substr(pos);
				bvalid = bsearchForName = true;
			}
		}
		else if (strcrit.find("NUMBER") == 0)
		{
			std::istringstream isn(strsearch.substr(pos + 1));
			if (isn >> number)
			{
				bvalid = true;
				bsearchForName = false;
			}
		}
	}
	return bvalid;
} 
std::string createFilename(const std::string & strprefix, const std::string & strext, int filenum = -1)
{
	if (filenum < 0)
		return strprefix + strext;
	std::ostringstream oss;
	oss << strprefix << "_" << filenum << strext;
	return oss.str();
}
bool performBinarySearch(std::string & strname, long long & number, bool & bsearchForName, std::string & strresult)
{
	long nbegin = 0;
	long nend = numRecords - 1;
	long nmid;
	char nm_got[100];
	unsigned int val_got;
	unsigned int nstop = 0;
	std::ifstream nameindexfile;
	std::ifstream numberindexfile;
	index_name idxnam = { 0 };
	index_number idxnum = { 0 };

	if (bsearchForName = true)
	{
		std::string strnameindexfile = createFilename("c:\\dp4\\flname", ".idx");
		nameindexfile.open(strnameindexfile.c_str(), std::ios::binary | std::ios::in);
		if (!nameindexfile.is_open())
			return -3; //
		if (!nameindexfile.read((char*)&idxnum, sizeof(index_name)))
			return -4; //
		ret = _stat64(strnameindexfile.c_str(), &fs64);
		if (ret != 0)
		{
			return -5;
		}
		numRecords = (int)(fs64.st_size / sizeof(index_name)); // make numRecords a size_t 

		char szArgv2[512] = { 0 };
		size_t ncharsConverted = 0;
		wcstombs(szArgv2, strname, sizeof(szArgv2));
		while (nbegin <= nend && nstop != -1)
		{
			nmid = (nbegin + nend) / 2;
			index_name rec = { 0 };
			nameindexfile.seekg(nmid* sizeof(index_name));
			nameindexfile.read((char*)&rec, sizeof(index_name));
			if (strcmp(szArgv2, rec.name)<0)
			{
				nend = nmid - 1;
				nmid = (nbegin + nend) / 2;
			}
			else
			{
				if (strcmp(szArgv2, rec.name)>0)
				{
					nbegin = nmid + 1;
					nmid = (nbegin + nend) / 2;
				}
				else
				{
					nstop = -1;
					strcpy(strresult, rec.getresult());
					return true;
				}
			}
		}
	}
	else
	{
		std::string strnumberindexfile = createFilename("c:\\dp4\\flnumber", ".idx");
		numberindexfile.open(strnumberindexfile.c_str(), std::ios::binary | std::ios::in);
		if (!numberindexfile.is_open())
			return -3; //
		if (!numberindexfile.read((char*)&idxnum, sizeof(index_number)))
			return -4; //
		ret = _stat64(strnumberindexfile.c_str(), &fs64);
		if (ret != 0)
		{
			return -5;
		}
		numRecords = (int)(fs64.st_size / sizeof(index_number)); // make numRecords a size_t 

		char szArgv2[512] = { 0 };
		size_t ncharsConverted = 0;
		wcstombs(szArgv2, strname, sizeof(szArgv2));
		while (nbegin <= nend && nstop != -1)
		{
			nmid = (nbegin + nend) / 2;
			index_number rec = { 0 };
			numberindexfile.seekg(nmid* sizeof(index_number));
			numberindexfile.read((char*)&rec, sizeof(index_number));
			if (strcmp(szArgv2, rec.number)<0)
			{
				nend = nmid - 1;
				nmid = (nbegin + nend) / 2;
			}
			else
			{
				if (strcmp(szArgv2, rec.number)>0)
				{
					nbegin = nmid + 1;
					nmid = (nbegin + nend) / 2;
				}
				else
				{
					nstop = -1;
					//strcpy(nm_got, rec.name);
					strcpy(strresult, rec.getresult());
					return true;
				}
			}
		}
	}

	return false;
}
void displayResult(bool & bfound, const std::string & strsearch, std::string & strresult)
{

}
int main(int nargs, char szargs[])
{
	long numRecords;
	std::string strsearch;
	bool bquit = false;

	while (bquit == false)
	{
		if (nargs == 1)
		{
			std::cout << "Enter your Search Criteria: (CTRL-Z to stop) " << std::endl;
			if (!std::getline(std::cin, strsearch) || strsearch == "q" || strsearch == "Q")
			{
				bquit = true;
				continue;
			}
		}
		// if a command was given we expect the search criteria to be passed as 1st argument
		else
		{
			strsearch = szargs[1];
			// we would leave the loop after query
			bquit = true;
		}
		// in both cases we parse the search criteria and if ok we perform it.
		std::string strname;
		long long number;
		bool bsearchForName = false;
		bool bvalid = parseSearchCriteria(strsearch, strname, number, bsearchForName);
		if (bvalid == false)
		{
			std::cout << std::endl << "[" << strsearch << "], "
				<< "wrong search criterium. Required syntax: 'Name=<name>' or 'Number=<number>'."
				<< std::endl << std::endl;
			continue;     // if nargs == 1 a new query can be entered.
		}
		std::string strresult;
		bool bfound = performBinarySearch(strname, number, bsearchForName, strresult);
		displayResult(bfound, strsearch, strresult);
	}
	return 0;
}

Open in new window

sarabandeCommented:
you didn't use my code (of function performBinarySearch) and the errors are only because of that.

it makes no sense to explain errors which are due to a usage of wrong or unneeded code.

you have to use the same functions as we had in savebinaryfile. you probably should move the commonly used functions to a new cpp file (binaryfilehelper.cpp) and put the structures to a header file.

if you run into issues not due to old code, you may post the full code and the errors and i will help you out.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Sorry Sara, I put

Name=...

and then I get the attached, while I'm having these codes
// 
//

#pragma warning (disable: 4996) 
#include "stdafx.h"
#include <set>
#include <sys/stat.h>
#include <string>
#include <fstream>
#include <sstream>
#include <atlbase.h>
#include <ctype.h>
#include <process.h>
#include <vector>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <Windows.h>
#include "..\..\include\struc4.h"

const unsigned int NUM_FILES = 80;
const unsigned int NUM_RECORDS = 1000000;
struct _stat64 fs64 = { 0 };
int ret; 
long numRecords;

struct index_name
{
	unsigned int recnum;
	char name[21];
	bool operator< (const index_name & ni) const
	{
		if (strcmp(name, ni.name) < 0) return true;
		return false;
	}
	std::string getresult() const { return name; }
};

struct index_number
{
	unsigned int recnum;
	long long number;
	bool operator< (const index_number & ni) const
	{
		if (number < ni.number) return true;
		return false;
	}
	std::string getresult() const { std::ostringstream oss; return ((std::ostringstream &)(oss << number)).str(); }
};

struct item
{
	char name[21];
	long long number;
	char description[100];
	bool operator<(const item & it) const
	{
		return (strcmp(name, it.name) < 0);
	}
};

struct fouritems
{
	item items[4];
	fouritems(bool bfill = false);

};

bool LessComp(const struc4& a1, const struc4& a2)
{
	if (strcmp(a1.item1, a2.item1) < 0) return true;
	if (strcmp(a1.item1, a2.item1) > 0) return false;
	if (a1.i1_int < a2.i1_int) return true;
	return false;
}
bool parseSearchCriteria(const std::string & strsearch,
	std::string & strname, long long & number, bool & bsearchForName)
{
	bool bvalid = false;
	// looking for NAME= or NUMBER=
	size_t pos = strsearch.find('=');
	if (pos != std::string::npos)
	{
		std::string strcrit(pos, '\0');
		std::transform(strsearch.begin(), strsearch.begin() + pos, strcrit.begin(), toupper);
		if (strcrit.find("NAME") == 0)
		{
			if ((pos = strsearch.find_first_not_of(" ", pos + 1)) != std::string::npos)
			{
				strname = strsearch.substr(pos);
				bvalid = bsearchForName = true;
			}
		}
		else if (strcrit.find("NUMBER") == 0)
		{
			std::istringstream isn(strsearch.substr(pos + 1));
			if (isn >> number)
			{
				bvalid = true;
				bsearchForName = false;
			}
		}
	}
	return bvalid;
} 
std::string createFilename(const std::string & strprefix, const std::string & strext, int filenum = -1)
{
	if (filenum < 0)
		return strprefix + strext;
	std::ostringstream oss;
	oss << strprefix << "_" << filenum << strext;
	return oss.str();
}
template <class index>
bool binarySearch(const std::string & strindexprefix, const index & searchindex, std::string & strresult, index & foundindex)
{
	std::string strindexfile = createFilename(strindexprefix, ".idx");
	std::ifstream indexfile(strindexfile.c_str(), std::ios::binary | std::ios::in);
	if (!indexfile.is_open())
	{
		strresult = "open error";
		return false;
	}
	index idx = { 0 };
	if (!indexfile.read((char*)&idx, sizeof(index)))
	{
		strresult = "read error";
		return false;
	}
	struct _stat64 fs64 = { 0 };
	int ret = _stat64(strindexfile.c_str(), &fs64);
	if (ret != 0)
	{
		strresult = "stat64 error";
		return false;
	}
	size_t numRecords = (int)(fs64.st_size / sizeof(index));

	long long nbegin = 0;
	long long nend = numRecords - 1;
	long long nmid = 0;
	while (nbegin <= nend)
	{
		nmid = (nbegin + nend) / 2;
		index indexrecord = { 0 };
		indexfile.seekg(nmid * sizeof(index));
		if (!indexfile.read((char*)&indexrecord, sizeof(index_name)))
		{
			strresult = "read error index record";
		}
		if (searchindex < indexrecord)
		{
			nend = nmid - 1;
			nmid = (nbegin + nend) / 2;
		}
		else
		{
			if (indexrecord < searchindex)
			{
				nbegin = nmid + 1;
				nmid = (nbegin + nend) / 2;
			}
			else
			{
				strresult = indexrecord.getresult();
				foundindex = indexrecord;
				return true;
			}
		}
	}

	return false;
}

bool performBinarySearch(const std::string & strname, long long number, bool & bsearchForName, std::string & strresult)
{
	bool bfound = false;
	unsigned int recnum = 0;
	if (bsearchForName)
	{
		index_name  searchindex = { 0 };
		index_name  foundindex = { 0 };

		strcpy_s(searchindex.name, sizeof(searchindex.name) - 1, strname.c_str());
		bfound = binarySearch<index_name>("c:\\dp4\\flname", searchindex, strresult, foundindex);
		recnum = foundindex.recnum;
	}
	else
	{
		index_number  searchindex = { 0 };
		index_number  foundindex = { 0 };
		searchindex.number = number;
		bool bfound = binarySearch<index_number>("c:\\dp4\\flnumber", searchindex, strresult, foundindex);
		recnum = foundindex.recnum;
	}
	// still open: if bfound is true we have to read recnum from data file 
	// use a function getresult to get a string result from fouritems
	// then you could add this to the strresult returned from binarySearch
	return bfound;
}
void displayResult(bool & bfound, const std::string & strsearch, std::string & strresult)
{
	if (bfound == true)
	{
		std::cout << "Result : " << strresult << " to Search String - '" << strsearch << "'";
	}
}
int main(int nargs, char szargs[])
{
	long numRecords;
	std::string strsearch;
	bool bquit = false;

	while (bquit == false)
	{
		if (nargs == 1)
		{
			std::cout << "Enter your Search Criteria: (CTRL-Z to stop) " << std::endl;
			if (!std::getline(std::cin, strsearch) || strsearch == "q" || strsearch == "Q")
			{
				bquit = true;
				continue;
			}
		}
		// if a command was given we expect the search criteria to be passed as 1st argument
		else
		{
			strsearch = szargs[1];
			// we would leave the loop after query
			bquit = true;
		}
		// in both cases we parse the search criteria and if ok we perform it.
		std::string strname;
		long long number;
		bool bsearchForName = false;
		bool bvalid = parseSearchCriteria(strsearch, strname, number, bsearchForName);
		if (bvalid == false)
		{
			std::cout << std::endl << "[" << strsearch << "], "
				<< "wrong search criterium. Required syntax: 'Name=<name>' or 'Number=<number>'."
				<< std::endl << std::endl;
			continue;     // if nargs == 1 a new query can be entered.
		}
		std::string strresult;
		bool bfound = performBinarySearch(strname, number, bsearchForName, strresult);
		displayResult(bfound, strsearch, strresult);
	}
	return 0;
}

Open in new window

tt7.png
sarabandeCommented:
you should run the program in the debugger. in menu debug - exceptions - win32 exceptions check the checkbox for 0xc0000005 access violation. if there is a pointer error, the debugger will break immediately if you do so, and you could check the call stack where the wrong pointer came from.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Hi,
Thanks a lot Sara. Is it better to add any try ... catch to the codes?
sarabandeCommented:
not really. access violation (what leads to a crash in release like the one you experienced) normally means that a pointer was null or corrupt. a try-catch may help that your program doesn't bring the crash message but I never saw that a program luckily could recover from such an error.

the readbinaryfile doesn't need much memory. you easily can build a debug version as well. then do f5 in the visual studio and you can run the program and when it crashes you could break into the debugger and the call stack shows where it crashed.

I will try to create a version with a few keys. can you post the last code of savebinaryfile?

Sara
sarabandeCommented:
by the way, did you have "packed" binary files for both savebinaryfile and readbinaryfile project?

by 'packed' I mean that you have chosen byte-alignment in c++ - code generation.

in any case, you must have same setting in readbinaryfile or the structures have wrong size.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Thanks a lot Sara. I did work exactly in the way we talked about through the previous thread, to create the files.
sarabandeCommented:
the crash is because of strcpy_s statement in performBinarySearch.

the size is passed as 20 but needs to be 21.

use the following statement instead to be safe:

strncpy_s(searchindex.name, sizeof(searchindex.name), strname.c_str(), _TRUNCATE);

Open in new window


additionally you should comment code which uses struc4.

then remove global variables NUM_FILES, NUM_RECORDS, fs64, ret, numRecords below include statements.

then remove 'long numRecords;' at begin of main function.

all those were redundant and not used.

Sara
sarabandeCommented:
I found one more bug:

main function has wrong argument for szargs. it is an array of char* not an array of char.

int main(int nargs, char * szargs[])

Open in new window


Sara
sarabandeCommented:
with the changes the performBinarySearch works as expected.

what still is open, that if bfound is true, you should open the big data file for read, use seekg to position  at the record position that was determined by the recnum of the found index and sizeof(fouritems). then read the record into a fouritems structure. finally close the big data file.

the displayResult should display the search criterium and the 4 items of the structure, each item in a new line with name, number and description.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many many thanks Sara. How can I use the relevant keys from flname.idx and flnumber.idx, to locate the four item records within the very big file?
sarabandeCommented:
// still open: if bfound is true we have to read recnum from data file
// use a function getresult to get a string result from fouritems
// then you could add this to the strresult returned from binarySearch

in performBinarySearch there is a local variable recnum set with the recnum of the found index record. that is done for both name and number index. because of that you can do (at end of performBinarySearch)

if (bfound)
{
       std::string strdatafile = createFilename("c:\\dp4\\flout", ".dat");
       std::ifstream datafile(strdatafile.c_str(), std::ios::binary | std::ios::in);
       long long recpos = recnum;
       recpos *= sizeof(fouritems);
       fouritems record(false);   // create empty record
       datafile.seekg(recpos);
       if (!datafile.read((char*)&record, sizeof(fouritems)))
       {
              strresult += "\nread error data record";
              return false;
       }
       datafile.close();
       std::ostringstream oss;
       oss << strresult << " found. data record: " << recnum << std::endl;
       for (int it = 0; it < 4; ++it)
       {
           oss << std::setw(20) << record.items[it].name << ' '
                  << std::setw(19) << std::right << record.items[it].number << ' '
                  << std::left << record.items[it].description << std::endl;
       }
       strresult = oss.str();
 }

Open in new window


note, the above loop to display the items of the record probably better may be moved to struct fouritems getdisplay member function. you also may consider to fill the strresult with the search criterion in main function and do not pass strresult to function binarySearch. if doing so, the final output would be the search criterion and the found record items.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many many thanks Sara. Should "recnum" be from the relevant .idx files?
sarabandeCommented:
you already have the code:

bool performBinarySearch(...)
{
    ...
    unsigned int recnum = 0;
    ...
    if (bSearchForName)
    {
          ...
          recnum = foundindex.recnum;
    }
    else
    {
        ...
        recnum = foundindex.recnum;
    }
    ....
    if (bfound)
    {
         ...
         long long recpos = recnum;
         recpos *= sizeof(fouritems);
         ...

Sara
HuaMin ChenProblem resolverAuthor Commented:
Let me update you. Thanks a lot Sara.
sarabandeCommented:
you didn't copy the implementation of fouritems structure from savebinaryfile:

fouritems::fouritems(bool bfill)
 {
       memset(this, 0, sizeof(fouritems));
       if (bfill == true)
       {
             for (int j = 0; j<4; ++j)
             {
                   createItemName(items[j].name, 21);
                   items[j].number = createItemNumber();
                   strcpy_s(items[j].description, 99, createItemText(99).c_str());
             }
       }
 }

Open in new window


that is necessary if you don't have move the structures and functions used by both to a common header and cpp file.

Sara
sarabandeCommented:
the implementation of randindex function was in savebinaryfile.cpp and you have to copy it if it needs to be called in readbinaryfile.cpp.

alternatively, you could use the default constructor of fouritems by using

struct fouritems
{
     item items[4];
     // don't provide a constructor, then the compiler will create a default constructor
};

...


 // the following is possible if no constructor is defined
fouritems record = { 0 };   // create empty record
datafile.seekg(recpos);
if (!datafile.read((char*)&record, sizeof(fouritems)))
...

Open in new window


the above code would not call randindex since readbinaryfile doesn't need to create new items but only read already existing items from file.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many many thanks Sara.
Why do I get nothing returned, when running it like the attached?
tt13.png
HuaMin ChenProblem resolverAuthor Commented:
BTW, I previously got that, using C++, is not the ideal way to develop Windows form, and we have to instead use C# for handling Windows forms. What do you think to this?

Have a great weekend!
sarabandeCommented:
using C++, is not the ideal way to develop Windows form, and we have to instead use C# for handling Windows forms.
you could use managed c++ which produces the same intermediate code as c# and vb.net.

but anyway, the current programs don't need windows forms and if you want to add a professional user interface to that you easily can turn the current programs to dll's and call them from any gui program easily and with minimal changes.

Why do I get nothing returned, when running it like the attached?
can you post the latest code of readbinaryfile.cpp and savebinaryfile.cpp? as already told it is crucial that both were using the same structures and the same structure alignment.

how did you know that zzzzwPyHjuSnxUNjhiZa was a valid key?

by the way, if you do 'type flout.dat' at the command line (you can stop by CTRL-Z) you would get a lot of valid keys which are not at the end of the index files but somewhere in the middle. my last test with 10 files and 100000 records each gave a .dat file which could be opened by notepad and i debugged the binary search which had a match after about 18 iterations.

Sara
HuaMin ChenProblem resolverAuthor Commented:
how did you know that zzzzwPyHjuSnxUNjhiZa was a valid key?

I can see such name does exist within the big name file. And now we should be able to search against whatever names that exists on the four items, right? Can we please check your message as well? Many many thanks Sara.
HuaMin ChenProblem resolverAuthor Commented:
Good day Sara,
Any advice to the current question? Many thanks to your help!
sarabandeCommented:
I checked the code you posted with the message. it has some flaws:

1. performBinarySearch has a long long as 4th argument (recnum).

but required is an unsigned int & type for two reasons. one is that record numbers are less than 4 billion. so an unsigned int is sufficient, both in the index structures and for searching. the other is that the argument must be an output argument because it is retrieved from index files. currently recnum was 0 and keeps to be 0 even in case of a found key.

2. displayResult needs a bool argument for bfound. it should be only input so you should pass by value and not by reference. then, type of recnum can be turned to unsigned int. both changes are not crucial.

3. displayResult fills a string with the result. but that string is never displayed. you need to add

    std::cout << strresult << std::endl;

at end of displayResult.

Sara

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
HuaMin ChenProblem resolverAuthor Commented:
Many many thanks Sara.

the other is that the argument must be an output argument because it is retrieved from index files.

where to put "output"?

it should be only input so you should pass by value and not by reference

Open in new window


what to adjust to current codes?
sarabandeCommented:
you make an argument an input/output argument by defining a reference type:

bool performBinarySearch(......., unsigned int & recnum, ....)

an argument passed by reference can be used to return a different contents to the caller where as an argument passed by value (type of argument doesn't have &) is only input since it is copied to a local variable in the called function.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Thanks Sara. Can I know how it is returning the details like

Enter your Search Criteria: (CTRL-Z to stop)
Name=zzzznfvRREKsQKTgORoC
zzzznfvRREKsQKTgORoC found. data record: 9841674
AmzjDPeBhkvleYLDobkd     354781179160394 loh gimys qo flo pecunt
zzzznfvRREKsQKTgORoC     509869220538500 furlem clihif
uiHnSDwZVKrGGzFXdoKK    1056703295247840 flysh chirti chuj knez rarlap gewna
PuEBwVqJDGkiYaIrhjES    1276740265996848 soq glab gojerg dixo

?
sarabandeCommented:
what do you mean? it looks as if the result was correct, isn't it?

Sara
HuaMin ChenProblem resolverAuthor Commented:
Yes, I will soon close this thread. Thanks a lot Sara!
HuaMin ChenProblem resolverAuthor Commented:
Many thanks Sara.
Do you think it is feasible to specifically check against only one name or number, as now there are 4 names and 4 numbers to each record?
sarabandeCommented:
we now have the fine result that you can search for 4 names and 4 64-bit integers to find one record which contains 4 completely different items.  so actually you have an arbitrary record hwere you have 8 keys. this would cover 99 percent of all database tasks. i rarely have seen more than 4 keys for a record.

if you want to search only for one item, you easily can adopt the current program by using a structure oneitem. that is what we started with.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Hi Sara,
within one of the detail number file, what are the numbers highlighted on the attached? Many thanks.
tt18.png
sarabandeCommented:
you have to look at the hex numbers on the left table. if you order a sequence of nonzero hex bytes from right to left and convert the result with a hex calculator (for example calc.exe) then you will get the number the sequence is representing.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many thanks Sara. Is this

@'£S¡Ò

one hex number?
HuaMin ChenProblem resolverAuthor Commented:
Hi Sara,
One last thing, I want to store unicode to description field. what to adjust below

fouritems::fouritems(bool bfill)
{
	memset(this, 0, sizeof(fouritems));
	if (bfill == true)
	{
		for (int j = 0; j<4; ++j)
		{
			createItemName(items[j].name, 21);
			items[j].number = createItemNumber();
			strcpy_s(items[j].description, 99, createItemText(99).c_str());
		}
	}
}

Open in new window

and I should alter the data type of whatever place, that is referring to "description" field, right? Many thanks.
HuaMin ChenProblem resolverAuthor Commented:
Good day Sara,
How are you? Can you please see this only if available? Many thanks to your help always!
sarabandeCommented:
probably the easiest is to return a std::wstring from createItemText.  

the steps to change are:

(1) change description from struct item to wchar_t
    (perhaps you want to reduce the length to 50 what is sufficient for 99 percent of the random texts).
(2) change return type of createItemText to std::wstring.
(3) at end of function replace the return statement by

       
std::string  strtext = os.str().substr(0, maxsize);
       std::wstring wstrtext(strtext.size()+1, L'\0');
       mbstowcs(&wstrtext[0], strtext.c_str(), strtext.size());
       return wstrtext;

Open in new window


(4) replace strcpy_s by wcscpy_s when calling createItemText.
(arguments keep unchanged).

we still could put the variable length texts (regardless whether they were Unicode or not) into an extra text file what would spare 50 percent or more. we even could flag the texts in the text file whether they contain Unicode characters or not and spare again up to 50 percent if we pack the non-Unicode strings to ansi.

note, utf-8 is a multi-byte character set which is mightier than utf-16 (wrongly named Unicode by MS). for ascii characters utf-8 needs exactly 1 char. because of that it is the most recommendable character set if the majority texts are English.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many many thanks Sara. How to correct last 2nd line below?

...
	std::string  strtext = os.str().substr(0, maxsize);
	std::wstring wstrtext(strtext.size() + 1, L'\0');
	mbstowcs(&wstrtext[0], strtext.c_str(), strtext.size());
	wstrtext.append("一般資料");
	return wstrtext;

Open in new window

sarabandeCommented:
How to correct last 2nd line below?
you mean this line?
wstrtext.append("一般資料");

Open in new window

actually I don't know exactly as I never used a Unicode codepage in visual studio myself.

I copied the statement to my code and got some warnings that my current codepage (1252) would not support these letters. then I could choose to store it as Unicode text (utf16). when compiling I got some more warnings which I could solve by adding an L prefix to the literal:

wstrtext.append(L"一般資料");

Open in new window


Sara
HuaMin ChenProblem resolverAuthor Commented:
Good day Sara,
BTW, within VS2013, for

struct fouritems
{
	item items[4];
...

Open in new window

what is the Max number to the array, for the struct in above?
Many thanks.
sarabandeCommented:
there is no physical limit only a logical one. how much keys do you need to identify and find one record?

I personally would prefer oneitem struct what also would meet the requirements of first normalform (1NF).

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many thanks Sara. I just want to see the Max number of items the array can have, within the struct. On VS 2013 C++, is there a limit to this?
sarabandeCommented:
no. you can use any integer size. however, for variables and arrays of the struct on the stack there might be limitations (in visual studio there is a property where you can define the maximum stack size). on the heap you might get problems with contiguous memory if you were using max number items greater - say - 100k.

as the number of items directly have an impact to the size of records you can't increase the number to a size greater 10 without violating the main design of your database. so, the c++ limitation is not an issue. it is comparable that strings could take up to billion times billion of characters but you rarely will use a string greater than 1000 characters and may get problems if using strings that have millions of characters.

Sara
HuaMin ChenProblem resolverAuthor Commented:
as the number of items directly have an impact to the size of records you can't increase the number to a size greater 10

Many thanks Sara. We can have many items within the struct called fouritems, to the following codes,

struct fouritems
{
	item items[4];
...

Open in new window


right?
HuaMin ChenProblem resolverAuthor Commented:
And I want to know its limit. Have a great day!
sarabandeCommented:
We can have many items within the struct called fouritems, to the following codes,
a struct called 'fouritems' should have 4 items, right? :-)

normally a record structure doesn't contain a fixed-sized array as only member. this concept violates both normalization principles of relational database and object-oriented principles since the items of a record obviously don't have any meaningful relation and are totally arbitrary. why should they be stored within one physical record? if you add more members (not increase the number of items) the design could be improved, however it still doesn't make a practical sense to use exactly a quadruple of items for each 'object' record even if you have some more members which may add some kind of identification to the record. in the real word you would have a dynamical number of items per record and in a relational database you would put this concept into a master-detail relationship.

if you consider these design aspects, your question for the maximum of items per record is somewhat strange. obviously there is no critical physical limitation beside of the fact that a structure needs contiguous space of memory (either on the stack or on the heap) and an array of  structures needs a multiple of this space. so, if you try to define 1 million of items in a structure, you may already get problems with one record let alone with an array of such records. if you consider the logical and practical aspects of such a design, things are worse. what do you want to do with such 'monster' records? you don't have benefits but only disadvantages.

so a much better design is to have structure item and then a  master structure which can take a dynamic number of items and has some more attributes beside of the items. then the 'limit' you asked for is only restricted by the system resources and nothing else.

Sara
HuaMin ChenProblem resolverAuthor Commented:
Many thanks Sara. Is there anything like "100" columns, "ideal" for one record? I mean just the limit of Max number of field to one record/struct.
sarabandeCommented:
i personally think that a structure that has more than 10 members is wrong design. even for 10 members you probably will have records which don't need all these attributes and others where you would need some extra information. I always would favor a good data(base) design (which normally has multiple entities and dynamic relationships) before technical simplicity but of course that might depend on the requirements.

Sara
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.