User Name Password Register
DaniWeb IT Discussion Community
All
What is DaniWeb IT Discussion Community?
You're currently browsing the C++ section within the Software Development category of DaniWeb, a massive community of 375,170 software developers, web developers, Internet marketers, and tech gurus who are all enthusiastic about making contacts, networking, and learning from each other. In fact, there are 2,237 IT professionals currently interacting right now! Registration is free, only takes a minute and lets you enjoy all of the interactive features of the site.
Please support our C++ advertiser:
Views: 946 | Replies: 9
Reply
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation: kinggarden is an unknown quantity at this point 
Rep Power: 2
Solved Threads: 0
kinggarden kinggarden is offline Offline
Newbie Poster

Solution Simple solution to database

  #1  
Jun 11th, 2007
Hi everybody:

I'm trying to do the practices on the top of forum. I started from

beginner's level. This time I will show you my solution to implement

a simple database in C++. If you could give some advice on the

following aspects or other, it would be so nice of you

1. base datastructure. I used a 2D array to store a number of

pointers to string data. However, it's not easy to resize. I find it's not

easy to add a column.

2. design pattern. My solution really looks like a hardcoded solution

only to this question. How to find a better a design pattern than could

be reused easily?

3.algothirms.

4. multithreads and process protection

5....

I hope my post is not too long to make you boring.

my codes:

  1.  
  2.  
  3. #include "stdafx.h"
  4. #include "stdio.h"
  5. #include "stdlib.h"
  6. #include "string.h"
  7. #include "io.h"
  8. const char *filename = "d:/database.txt";
  9. const int ROWS = 10;
  10. const int COLOUMS = 3;
  11. class Data
  12. {
  13. public:
  14. Data() :
  15. m_nTotalColumns(COLOUMS),
  16. m_nTotalRows(ROWS),
  17. m_nCurCol(0),
  18. m_nCurRow(0),
  19. m_ppcData(NULL),
  20. m_fp(NULL)
  21. {
  22. }
  23.  
  24. virtual ~Data() {}
  25.  
  26. //create a dynamic 2 dimension char * array which points to a single data string
  27. bool Init() {
  28. m_ppcData = (char **)new char *[ROWS][COLOUMS];
  29. if(NULL == m_ppcData) {
  30. return false;
  31. }
  32. memset((void *)m_ppcData, NULL, ROWS * COLOUMS * sizeof(char *));
  33. return ReadFile();
  34. }
  35.  
  36. //insert a record with all values of fields
  37. void Insert(const char *name, const char *Gender, const char *old) {
  38. if (NULL == name || NULL == Gender || NULL == old) {
  39. return;
  40. }
  41. //array is full
  42. if(++m_nCurRow >= m_nTotalRows) {
  43. return;
  44. }
  45. StrCpy(m_ppcData[m_nCurRow * m_nTotalColumns], name);
  46. StrCpy(m_ppcData[m_nCurRow * m_nTotalColumns + 1], Gender);
  47. StrCpy(m_ppcData[m_nCurRow * m_nTotalColumns + 2], old);
  48. }
  49.  
  50. //updata a special data
  51. void Update(const unsigned int row, const unsigned int column, const char *data) {
  52. if(row > m_nCurRow || column >= m_nTotalColumns || NULL == data) {
  53. return;
  54. }
  55. StrCpy(m_ppcData[row * m_nTotalColumns + column], data);
  56. }
  57.  
  58. //select a special data
  59. char *Select(const unsigned int row, const unsigned int column) {
  60. if(row > m_nCurRow || column >= m_nTotalColumns) {
  61. return NULL;
  62. }
  63. return m_ppcData[row * m_nTotalColumns + column];
  64. }
  65.  
  66. //delete a row
  67. void DeleteRecord(const unsigned int row) {
  68. if(row > m_nCurRow) {
  69. return;
  70. }
  71. unsigned int i;
  72. unsigned int j;
  73. for(i = 0; i < m_nTotalColumns; i++) {
  74. delete [] m_ppcData[row * m_nTotalColumns + i];
  75. m_ppcData[row * m_nTotalColumns + i] = NULL;
  76. }
  77. //if the deleted row is not the last, i will fflush the data to the top
  78. if(row < m_nCurRow) {
  79. for(i = row; i < m_nCurRow; i++) {
  80. for(j = 0; j < m_nTotalColumns; j++) {
  81. m_ppcData[i * m_nTotalColumns + j] = m_ppcData[(i + 1) * m_nTotalColumns + j];
  82. }
  83. }
  84. }
  85. memset((void *)(m_ppcData + m_nCurRow * m_nTotalColumns), NULL, COLOUMS * sizeof(char *));
  86. m_nCurRow--;
  87. }
  88.  
  89. void display() {
  90. printf("**************\n");
  91. for(int i = 0; i < ROWS * COLOUMS; ++i) {
  92. if (NULL != m_ppcData && NULL != m_ppcData[i]) {
  93. printf("%s\n ", m_ppcData[i]);
  94. }
  95. }
  96. }
  97.  
  98. void Save() {
  99. WriteFile();
  100. }
  101. protected:
  102. const char* StrCpy(char *&dest, const char *src) {
  103. if(NULL == src) {
  104. return NULL;
  105. }
  106. //avoid copy self
  107. if(src == dest) {
  108. return dest;
  109. }
  110. //release original memory
  111. if(NULL != dest) {
  112. delete [ ]dest;
  113. dest = NULL;
  114. }
  115. int len = strlen(src);
  116. dest = new char[len + 1];
  117. if(NULL == dest) {
  118. return NULL;
  119. }
  120. return strcpy(dest, src);
  121. }
  122. //write data into file
  123. bool WriteFile() {
  124. m_fp = fopen(filename, "w+");
  125. if(NULL == m_fp) {
  126. return false;
  127. }
  128. for(int i = 0; i < ROWS * COLOUMS; ++i) {
  129. if (NULL != m_ppcData && NULL != m_ppcData[i]) {
  130. fputs(m_ppcData[i], m_fp);
  131. fputs(" ", m_fp);
  132. }
  133. }
  134. fclose(m_fp);
  135. return true;
  136. }
  137. //Read data From file
  138. //load all the data from the disk, do I need to do so?
  139. bool ReadFile() {
  140. m_fp = fopen(filename, "r+");
  141. if(NULL == m_fp) {
  142. return false;
  143. }
  144. long length = _filelength(_fileno(m_fp)) + 1;
  145. char *tmp = new char[length];
  146. if(NULL == tmp) {
  147. return false;
  148. }
  149. fgets(tmp, length, m_fp);
  150. int i = 0;
  151. char *pre = tmp;
  152. char *t = tmp;
  153. unsigned int pos;
  154. bool bIsEOF = false;
  155.  
  156. for(i = 0, pos = 0; (i < length) && (pos < m_nTotalRows * m_nTotalColumns); ++i) {
  157. if(*t == '\0' || *t == ' ') {
  158. //if we come to the end of the data, break
  159. if(*t == '\0') {
  160. StrCpy(m_ppcData[pos], pre);
  161. break;
  162. }
  163. else {
  164. *t = '\0';
  165. StrCpy(m_ppcData[pos], pre);
  166. //deal with multi spaces
  167. while (*++t == ' ') {
  168. }
  169. pre = t;
  170. pos++;
  171. }
  172. }
  173. &nref="http://bbs.fdc.com.cn/boardlist.asp?boardid=376" target=_blank>清江花园 [url="http://bbs.fdc.com.cn/boardlist.asp?boardid=277"]秦园居[/url] </DIV> <DIV class=tabcontent id=tcontent5> p; delete [] tmp;
  174. fclose(m_fp);
  175. return true;
  176. }
  177.  
  178. private:
  179. const unsigned int m_nTotalColumns;
  180. const unsigned int m_nTotalRows;
  181. unsigned int m_nCurCol;
  182. unsigned int m_nCurRow;
  183. FILE *m_fp;
  184. char **m_ppcData;
  185. };
  186.  
  187. int main(int argc, char* argv[])
  188. {
  189. Data d;
  190. if (d.Init()) {
  191. d.Update(2, 2, "100");
  192. d.Insert("Rock", "male", "24");
  193. d.display();
  194. char * p = d.Select(3, 2);
  195. printf("p = %s\n", p);
  196. d.DeleteRecord(1);
  197. d.display();
  198. d.Save();
  199. }
  200. return 0;
  201. }
  202.  
  203.  

You have to create a file called "d:/database.txt" on your disk, the contens look like this"name Gender old Rock male 100".

Thankyou very much for watching.
Rock Mu

I will keep walking!

Shanghai, China
AddThis Social Bookmark Button
Reply With Quote  
Join Date: Aug 2005
Location: near St Louis, Missouri, USA
Posts: 10,199
Reputation: Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of 
Rep Power: 34
Solved Threads: 824
Moderator
Featured Poster
Ancient Dragon's Avatar
Ancient Dragon Ancient Dragon is offline Offline
Most Valuable Poster

Re: Simple solution to database

  #2  
Jun 11th, 2007
don't use c style file i/o and strings in c++ programs. Yes its valid to do that but c++ file i/o and c++ string class are generally easier to use and makes your program less buggy.

c++ vectors and lists are the containers that will make it almost trivel to resize. You can create a 2d vector (rows and columns) by using a vector of vectors, something like this.
typedef vector<string> COLUMNS;
vector<COLUMS> rows;
Last edited by Ancient Dragon : Jun 11th, 2007 at 7:14 am.
'Politics' is made up of two words, 'poli,' which is Greek for 'many,' and 'tics,' which are blood-sucking insects.
- Gore Vidal
Being ignorant is not so much a shame as being unwilling to learn. - Benjamin Franklin
Reply With Quote  
Join Date: Feb 2007
Location: Bangalore, India
Posts: 535
Reputation: thekashyap will become famous soon enough thekashyap will become famous soon enough 
Rep Power: 4
Solved Threads: 50
thekashyap's Avatar
thekashyap thekashyap is offline Offline
Posting Pro

Re: Simple solution to database

  #3  
Jun 11th, 2007
A few comments:
1. Given it's C++ problem, try using C++ containers (instead of array) e.g. vector/list/map.
2. You don't really need to force use of a design. Not yet at least.
3. Comment on the design: Class Data should not be responsible for insertion/deletion/.. Look at it purely from english pov, Data itself shouldn't have this functionality. If you look at your problem stmt, you'll see that you're missing a major class (named Database). Database should be the one who has this functionality.
Are you Agile.. ?
Reply With Quote  
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation: kinggarden is an unknown quantity at this point 
Rep Power: 2
Solved Threads: 0
kinggarden kinggarden is offline Offline
Newbie Poster

Re: Simple solution to database

  #4  
Jun 13th, 2007
Thanks for all of you.

I tried to give a more c++ solution in the following. Acturally, it's the

first time I use some STL and exception mechanism in my codes.

So it would so nice of you to give me some advice on my codes

  1.  
  2. #include "stdafx.h"
  3. #pragma warning(disable:4786)
  4. #include <vector>
  5. #include <iostream>
  6. #include <fstream>
  7. #include <string>
  8. #include <algorithm>
  9. #include <iterator>
  10. #define RECORDMAXLENGstringH 100
  11. using namespace std;
  12. //template <class string>
  13. class Database
  14. {
  15. public:
  16. Database() {
  17. }
  18. virtual ~Database() {
  19. }
  20. Database(const Database &d) {
  21. m_vRow = d.m_vRow;
  22. }
  23. const Database& operator=(const Database& d) {
  24. if (this != &d) {
  25. this->m_vRow = d.m_vRow;
  26. }
  27. return *this;
  28. }
  29. const string& Select(const int r, const int c) {
  30. if(r >= m_vRow.size()) {
  31. throw r;
  32. }
  33. if(c >= m_vRow[0].size()) throw c;
  34. cout<<m_vRow[r][c]<<"\n";
  35. return m_vRow[r][c];
  36. }
  37. bool Update(const int r, const int c, const string & Data) {
  38. if(r >= m_vRow.size()) return false;
  39. if(c >= m_vRow[0].size()) return false;
  40. m_vRow[r][c] = Data;
  41. }
  42. bool DeleteRecord(const int r) {
  43. if(r >= m_vRow.size()) throw r;
  44. int i = 0;
  45. ROW::iterator itr = m_vRow.begin();
  46. while (i++ < r) {
  47. itr++;
  48. }
  49. m_vRow.erase(itr);
  50. return true;
  51. }
  52. void AddRecord(const string & name, const string& gender, const string &old) {
  53. COLUMN column;
  54. column.push_back(name);
  55. column.push_back(gender);
  56. column.push_back(old);
  57. m_vRow.push_back(column);
  58. }
  59. bool Init() {
  60. fstream fs("d:/database.txt", ios::in);
  61. string line;
  62. COLUMN column;
  63. //get a line from file and analyse the words in it
  64. while (getline(fs, line, '\n')) {
  65. string::size_type pos = 0;
  66. string::size_type pre = 0;
  67. string record;
  68. //when i find a space, it means I find a whole word
  69. while ((pos = line.find(' ', pos)) != string::npos) {
  70. record = line.substr(pre, pos - pre);
  71. pre = ++pos;
  72. //ignore multispace
  73. if (record != "") {
  74. column.push_back(record);
  75. }
  76. }
  77. //deal with the last word end with '\0'
  78. record = line.substr(pre, string::npos);
  79. column.push_back(record);
  80. m_vRow.push_back(column);
  81. column.clear();
  82. }
  83. fs.close();
  84. return false;
  85. }
  86. bool Save() {
  87. fstream fs("d:/database.txt", ios::trunc | ios::out);
  88. COLUMN::iterator itc;
  89. ROW::iterator itr;
  90. for(itr = m_vRow.begin(); itr != m_vRow.end(); itr++) {
  91. for(itc = (*itr).begin(); itc != (*itr).end(); itc++) {
  92. fs<<(*itc)<<' ';
  93. }
  94. fs<<'\n';
  95. }
  96. fs.close();
  97. return false;
  98. }
  99. void display() {
  100. COLUMN::iterator itc;
  101. ROW::iterator itr;
  102. for(itr = m_vRow.begin(); itr != m_vRow.end(); itr++) {
  103. for(itc = (*itr).begin(); itc != (*itr).end(); itc++) {
  104. cout<<*itc<<' ';
  105. }
  106. cout<<'\n';
  107. }
  108. }
  109. private:
  110. typedef vector<string> COLUMN;
  111. typedef vector<COLUMN> ROW;
  112. ROW m_vRow;
  113. //const static char* m_sFilename;
  114. };
  115. //template <class string>
  116. //string
  117. //const char* Database::m_sFilename = "d:/database.txt";
  118. int main(int argc, char* argv[])
  119. {
  120. Database database;
  121. try {
  122. database.Init();
  123. database.display();
  124. database.AddRecord("Chirly", "female", "18");
  125. database.display();
  126. database.Select(3, 1);
  127. database.DeleteRecord(5);
  128. database.display();
  129. database.Save();
  130. }
  131. catch (int i) {
  132. cout<<i<<"too large\n";
  133. }
  134. catch(bad_alloc &) {
  135. cout<<"bad alloc\n";
  136. }
  137.  
  138. return 0;
  139. }
  140.  


Ps: the definitions of list, vector.. in headers LIST, VECTOR...

are really difficult to understand. Why does that coder write that

complicated codes? To avoid the codes being understood by others?

Rock Mu

I will keep walking!

Shanghai, China
Reply With Quote  
Join Date: Sep 2004
Posts: 6,009
Reputation: Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of 
Rep Power: 26
Solved Threads: 413
Super Moderator
Narue's Avatar
Narue Narue is offline Offline
Expert Meanie

Re: Simple solution to database

  #5  
Jun 13th, 2007
>So it would so nice of you to give me some advice on my codes
Granted, all I did was skim over it, but your use of the standard library and exceptions looks okay.

>Why does that coder write that complicated codes?
Well, it looks like you're using Visual Studio, and the standard library code for Visual Studio is notoriously obtuse. But there's always going to be some measure of complexity in code that does so much with templates.
Member of: Beautiful Code Club.
Reply With Quote  
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation: kinggarden is an unknown quantity at this point 
Rep Power: 2
Solved Threads: 0
kinggarden kinggarden is offline Offline
Newbie Poster

Re: Simple solution to database

  #6  
Jun 13th, 2007


  1.  
  2. const string& Select(const int r, const int c) {
  3. if(r >= m_vRow.size()) {
  4. throw r;
  5. }
  6. if(c >= m_vRow[0].size()) throw c;
  7. cout<<m_vRow[r][c]<<"\n";
  8. return m_vRow[r][c];
  9. }
  10.  

In fact, in this function, I tried to return something instead of

throw something. But it's not easy to return something, I have to

return a string object if r >= m_vRow.size() is true. What to return?

I have no idea.
Last edited by kinggarden : Jun 13th, 2007 at 10:15 am.
Rock Mu

I will keep walking!

Shanghai, China
Reply With Quote  
Join Date: Sep 2004
Posts: 6,009
Reputation: Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of 
Rep Power: 26
Solved Threads: 413
Super Moderator
Narue's Avatar
Narue Narue is offline Offline
Expert Meanie

Re: Simple solution to database

  #7  
Jun 13th, 2007
>What to return?
An exception is the better option in this case. But if you want to return something, you need to pick a string that couldn't possibly be in the database. That's difficult to do, so you might do well to add a level of indirection by using an object for each cell rather than a string. Then you can easily give it a null value and any other cellish information that isn't easily derived from the value:
class Cell {
  bool is_null;
  std::string value;
public:
  Cell ( const std::string& init = "" )
    : is_null ( init.size() == 0 ), value ( init )
  {}
public:
  const std::string& GetValue() const
  {
    return value;
  }

  void SetValue ( const std::string& s )
  {
    value = s;
    is_null = false;
  }

  bool IsNull() const
  {
    return is_null;
  }
};
Member of: Beautiful Code Club.
Reply With Quote  
Join Date: Aug 2005
Location: near St Louis, Missouri, USA
Posts: 10,199
Reputation: Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of Ancient Dragon has much to be proud of 
Rep Power: 34
Solved Threads: 824
Moderator
Featured Poster
Ancient Dragon's Avatar
Ancient Dragon Ancient Dragon is offline Offline
Most Valuable Poster

Re: Simple solution to database

  #8  
Jun 13th, 2007
Originally Posted by kinggarden View Post

Ps: the definitions of list, vector.. in headers LIST, VECTOR...

are really difficult to understand. Why does that coder write that

complicated codes? To avoid the codes being understood by others?



I agree -- and I don't bother any more trying to read those header files. You can find better explanations for the stl classes with google. And you can find some example programs with google too. Also check out the c++ code snippets here at DaniWeb.
'Politics' is made up of two words, 'poli,' which is Greek for 'many,' and 'tics,' which are blood-sucking insects.
- Gore Vidal
Being ignorant is not so much a shame as being unwilling to learn. - Benjamin Franklin
Reply With Quote  
Join Date: Jun 2007
Location: Shanghai
Posts: 16
Reputation: kinggarden is an unknown quantity at this point 
Rep Power: 2
Solved Threads: 0
kinggarden kinggarden is offline Offline
Newbie Poster

Re: Simple solution to database

  #9  
Jun 13th, 2007
Originally Posted by Narue View Post
>What to return?
An exception is the better option in this case. But if you want to return something, you need to pick a string that couldn't possibly be in the database. That's difficult to do, so you might do well to add a level of indirection by using an object for each cell rather than a string. Then you can easily give it a null value and any other cellish information that isn't easily derived from the value:
class Cell {
  bool is_null;
  std::string value;
public:
  Cell ( const std::string& init = "" )
    : is_null ( init.size() == 0 ), value ( init )
  {}
public:
  const std::string& GetValue() const
  {
    return value;
  }
 
  void SetValue ( const std::string& s )
  {
    value = s;
    is_null = false;
  }
 
  bool IsNull() const
  {
    return is_null;
  }
};


Thank u very much.

But I still have a question, if I write something like this

  1.  
  2. const Cell & Test() {
  3.  
  4. return NULL; //I just want to tell something is wrong
  5. }
  6.  
  7.  

Of course, I defined a copy constructor in Class Cell like this

  1. Cell(const int i) {
  2. if(0 == i) is_null =true;
  3. }

But it doesn't work well, there is a waring "returning address of local

variable or temporary". I know that, a temp object is generated in

Test and the reference of that object is returned.

In fact, i think Test() returns a reference of something, it must be

a long life object(i mean not temp or local). But at that time, i don't

have such a object, how to return?

Thank you
Rock Mu

I will keep walking!

Shanghai, China
Reply With Quote  
Join Date: Sep 2004
Posts: 6,009
Reputation: Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of Narue has much to be proud of 
Rep Power: 26
Solved Threads: 413
Super Moderator
Narue's Avatar
Narue Narue is offline Offline
Expert Meanie

Re: Simple solution to database

  #10  
Jun 15th, 2007
>const Cell & Test() {
> return NULL; //I just want to tell something is wrong
There's no such thing as a null reference in C++. If all you want to do is return a single error state, you can follow the same pattern as the iostream library:
operator void*() const
{
  return ( is_null ) ? 0 : this;
}
This returns a null pointer if the cell is null, or a pointer to the object if it's not. If you aren't comfortable working with overloaded type conversions, you can do the same thing with Test:
void *Test() const
{
  return ( is_null ) ? 0 : this;
}
Member of: Beautiful Code Club.
Reply With Quote