3D 中的动态字符串数组
本文介绍了创建和管理一维、二维和三维字符串数组的方法
引言
好的,先来个介绍... 我已经很久没写文章了,所以不知道我的写作技巧还在不在,总之...
这篇小文章是关于多维 string
数组的。只针对 String
数组,因为已经有很多关于整数数组的技巧了,但关于这篇文章的主题的信息不多,而且,完全没有现成的代码,你可以直接复制粘贴到你的项目中,所以我们开始吧。所以,是的,我不会写一篇关于(!非常重要!)内存管理和其他事情的冗长的技术故事 - 我只会给你一个类,你只需要使用它就可以了。但是,我并没有声明它能防内存泄漏,可能还会有错误和其他问题,但这就是你得到源代码来玩的原因。
关于 C 中的内存管理,我只想说:如果你分配了内存 - 就释放它。到处都要释放。否则内存泄漏是不可避免的。所以,我告诉你我将给你一个类。好吧,我把整个东西称为一个类,但这个类实际上只是一个对常规 C 函数的包装类。而且,我的类还提供了额外的功能 - 用户可以提供自己的指针。为了什么?嗯,例如,你可以从不同的语言中传递你的指针。
考虑一下这种情况
wchar_t *test()
{
static wchar_t *a = (wchar_t *)malloc(MAX_PATH);
wcscpy(a, L"something");
return a;
}
和这个
void test(wchar_t *&pointer)
{
pointer = (wchar_t *)malloc(MAX_PATH);
wcscpy(pointer, L"something");
}
所以,你可以传递你的指针,让类来管理它,然后你就可以自己对它做任何你想做的事情。上面的例子有点傻,但这里我谈论的是 3D 数组,所有这些都可能变得有点棘手。
还有一件事,你可能会问,为什么我使用了古老的 C 而不是拥有所有优点的 C++。首先,因为这个类 (ArrayWorker
) 使用原生类型:char
和 wchar_t
,所以再说一遍,你从它那里得到的任何输出 - 你都可以很容易地在不同的语言之间共享,比如 C 和 C#。所以,当我看了看 C++ 以及我需要进行多少转换时,我决定专注于 C。
最后一件事。ArrayWorker
的安排方式让你不仅限于 3D,你可以把它做成 100D - 这完全是关于添加一个更多的间接级别。
背景
那么,什么是 string
数组呢?你可能也知道。什么是多维数组?你可能也知道。
wchar_t *String = L"some string";
wchar_t *Array1D[2] = {L"some", L"string"};
wchar_t *Array2D[2][2] =
{
{L"some", L"String"},
{L"some other", L"string"}
}
wchar_t *Array3D[2][2][2] =
{
{
{L"some", L"string"},
{L"nice", L"string"}
},
{
{L"another", L"String"},
{L"just cut", L"this already"}
}
}
等等。这些是 static
数组 - 相当容易的东西。1D 数组就像一个 string
的容器,2D 数组很像一个表格,而 3D 数组几乎就像一个数据库。现在,我们需要做的就是创建这样的数组,但动态数组,具有动态内存分配、重新分配、释放、删除 string
、替换 string
、添加 string
等等。有人很容易在字节中迷失方向,并放弃在 C# 中使用 StringBuilder
,但我们不会,同志们,我们要继续前进。
Using the Code
这就是我们目前所拥有的。
// ArrayWorker::_1D, public methods:
public:
_1D(IN BOOL UserArray = FALSE); // if you want to supply own pointer, use TRUE
~_1D();
// if you want to supply own pointer, you have to initialize it
VOID InitUserArray(IN wchar_t **&uArray);
BOOL Append(IN std::string Temp);
// if you are using your own pointer, you have to add it to every public method
// std::wstring and std::string will be converted to wchar_t, like char as well
BOOL Append(IN std::string Temp, IN OUT wchar_t **&uArray);
BOOL Append(IN std::wstring Temp);
BOOL Append(IN std::wstring Temp, IN OUT wchar_t **&uArray);
BOOL Append(IN char *String);
BOOL Append(IN char *String, IN OUT wchar_t **&uArray);
BOOL Append(__in wchar_t *String);
BOOL Append(__in wchar_t *String, IN OUT wchar_t **&uArray);
unsigned int Length(VOID); // get a length of all strings together
unsigned int Length(IN wchar_t **uArray, IN int count = -1); // get length of
// your own array
// usage: ToArray("one,two,three", ",");
// string will be parsed and each element will be added to array
void ToArray(IN char *String, IN char *Delimiter);
// usage: wchar_t **myarray; ToArray("one,two,three", ",", myarray);
void ToArray(IN char *String, IN char *Delimiter, IN OUT wchar_t **&uArray);
void ToArray(IN wchar_t *String, IN wchar_t *Delimiter);
void ToArray(IN wchar_t *String, IN wchar_t *Delimiter, IN OUT wchar_t **&uArray);
// ansi to unicode conversion; just a helper function
wchar_t * AStringToW(IN char * pstr);
// unicode to ansi conversion; just a helper function
char * WStringToA(IN wchar_t * pstr);
BOOL NullOrEmpty(IN wchar_t *String); // like you got in C#, helper function
BOOL NullOrEmpty(IN char *String);
// Usage: ToString(",");
// returns: one,two,three
char * ToString(IN char *Delimiter); // converts array to a string,
// where elements are separated with provided delimiter
char * ToString(IN char *Delimiter, IN wchar_t **uArray, IN int Elems = -1);
wchar_t * ToString(IN wchar_t *Delimiter);
wchar_t * ToString(IN wchar_t *Delimiter, IN wchar_t **uArray, IN int Elems = -1);
// replaces element in array with other element
// if replAtIndex provided, replaces at specific position only
// else can replace all instances of same string
// usage: Replace("something", "nothing", FALSE/*complete string is known*/,
// TRUE/*wipe out every "something" strings*/, -1);
BOOL Replace(IN char *Search, IN char *Replace,
IN BOOL isSearchPartial = FALSE,
IN BOOL replaceAll = FALSE,
IN int replAtIndex = -1);
// same like above, but you can provide your own array
BOOL Replace(IN char *Search, IN char *Replace,
IN OUT wchar_t **&uArray,
IN int Elems = -1,
IN BOOL isSearchPartial = FALSE,
IN BOOL replaceAll = FALSE,
IN int replAtIndex = -1);
// unicode version
BOOL Replace(IN wchar_t *Search,IN wchar_t *Replace,
IN BOOL isSearchPartial = FALSE,
IN BOOL replaceAll = FALSE,
IN int replAtIndex = -1);
BOOL Replace(IN wchar_t *Search,IN wchar_t *Replace,
IN OUT wchar_t **&uArray,
IN int Elems = -1,
IN BOOL isSearchPartial = FALSE,
IN BOOL replaceAll = FALSE,
IN int replAtIndex = -1);
// get string at index
wchar_t * GetAtIndex(IN int Index);
wchar_t * GetAtIndex(IN int Index, IN wchar_t **uArray);
char * GetAtIndexA(IN int Index); // ansi version
char * GetAtIndexA(IN int Index, IN wchar_t **uArray);
// this one is like replace, but it removes string
BOOL Remove(IN wchar_t *Item = NULL,
IN int Index = -1,
IN BOOL isItemPartial = FALSE,
IN BOOL removeAll = FALSE);
BOOL Remove(IN wchar_t **&uArray,
IN int Elems = -1,
IN wchar_t *Item = NULL,
IN int Index = -1,
IN BOOL isItemPartial = FALSE,
IN BOOL removeAll = FALSE);
// this will return complete array
// usage: wchar_t **arr = ar->GetArray(); wprintf(L"%s %s %s", arr[0], arr[1], arr[2]);
wchar_t **GetArrayW();
int Count(VOID); // counts array elements
int Count(IN wchar_t **uArray);
void Clear(VOID); // clears array, so now it has 0 elements and is reusable
void Clear(IN OUT wchar_t **&uArray, IN OUT int Elems = -1);
void Destroy(VOID); // destroys array, its instance is not usable anymore
void Destroy(IN OUT wchar_t **&uArray, IN OUT int Elems = -1);
这是 1D 数组的。现在我们来看 2D 和 3D 数组。
// ArrayWorker::_2D public methods
// only unicode is supported
_2D(IN BOOL UserTable = FALSE);
~_2D();
void InitUserTable(IN OUT wchar_t ***&uTable); // you are supplying your
// own pointer "uTable"
void AppendRow(IN wchar_t **Row); // add row to a table = add 1D array to 2D array
void AppendRow(IN wchar_t **Row, IN OUT wchar_t ***&uTable);
void ReplaceRow(IN wchar_t **NewRow, IN int Index);
void ReplaceRow(IN wchar_t **NewRow, IN int Index, IN OUT wchar_t ***&uTable);
void RemoveRow(IN int Index);
void RemoveRow(IN int Index, IN OUT wchar_t ***&uTable);
unsigned int Size();
unsigned int Size(IN wchar_t ***uTable);
wchar_t ***GetTable();
int CountRows();
int CountRows(IN wchar_t ***uTable);
void Clear(VOID);
void Clear(IN OUT wchar_t ***&uTable, IN int Elems = -1);
void Destroy(VOID);
void Destroy(IN OUT wchar_t ***&uTable, IN int Elems = -1);
// ^ methods names speak for themselfs, right?
// 3D array has almost the same methods, the only difference is level of indirection
// based on such construction, you can extend dimensions of array.
// ArrayWorker::_3D public methods
_3D(IN BOOL UserDb = FALSE);
~_3D();
void InitUserDb(IN OUT wchar_t ****&uDb); // uDb is your pointer
void AppendTable(IN wchar_t ***Tab);
void AppendTable(IN wchar_t ***Tab, IN OUT wchar_t ****&uDb);
void ReplaceTable(IN wchar_t ***NewTable, IN int Index);
void ReplaceTable(IN wchar_t ***NewTable, IN int Index, IN OUT wchar_t ****&uDb);
void RemoveTable(IN int Index);
void RemoveTable(IN int Index, IN OUT wchar_t ****&uDb);
wchar_t ****GetDataBase(); // returns 3D array, db->GetDataBase()[table][row][cell];
unsigned int Size();
unsigned int Size(IN wchar_t ****uDb);
int CountTables();
int CountTables(IN wchar_t ****uDb);
void Clear(VOID);
void Clear(IN OUT wchar_t ****&uDb, IN int Elems = -1);
void Destroy(VOID);
void Destroy(IN OUT wchar_t ****&uDb, IN int Elems = -1);
就这样。正如你所看到的,有重载,比如 Clear
方法。所以,这个 "uArray
"、"uTable
" 和 "uDb
" 是你的指针。还有一件事,有时候,会像这样有参数:IN int Elems = -1 - 这里是解释。基本上,在添加元素时,类会计算它们,但例如,如果你提供自己的数组并且你已经做了自己的计数,那么你也可以提供它们。几乎在 100% 的情况下,这个参数都是未使用的。
好的,让我们来测试一下。
void Array1DTest(int loops) // 100.000 loops
{
ArrayWorker::_1D *row1 = new ArrayWorker::_1D();
std::string stdtest = "somestd";
std::wstring stdwstr = L"unicode";
for(int i = 0; i <= loops; i++)
{
wprintf(L"Lopp nr: %d\n", i);
row1->Append(stdtest);
row1->Append(stdwstr);
row1->Append("char");
row1->Append(L"wchar");
int len = row1->Length();
char *tempstr = row1->ToString(",");
row1->ToArray(tempstr, ",");
wchar_t *wtempstr = row1->ToString(L",");
row1->ToArray(wtempstr, L",");
row1->Replace("char",
"newchar1111111111111111111111111111111111111111x");
row1->Replace(L"wchar",
L"newwchar2222222222222222222222222222222222222222222222z");
row1->Remove(NULL, 0);
row1->Remove(NULL, 0);
free(tempstr);
free(wtempstr);
if(i < loops)row1->Clear();
}
wchar_t **myrow = row1->GetArrayW();
wprintf(L"\r\n%s | %s\r\n", myrow[0], myrow[1]); // newchar and newwchar
delete row1;
}
void Array2DTest(int loops) // 100.000 loops
{
ArrayWorker::_2D *Table = new ArrayWorker::_2D();
wchar_t *testrow[] = {L"some", L"row", L"here", L"man"};
wchar_t *somerow[] = { L"well", L"done", L"my", L"friend"};
wchar_t *nicerow[] = { L"looks", L"like", L"its", L"done" };
for(int i = 0; i <= loops; i++)
{
wprintf(L"Loop nr: %d\n", i);
Table->AppendRow(testrow);
Table->AppendRow(somerow);
Table->RemoveRow(0);
Table->ReplaceRow(nicerow, 0);
if(i < loops)Table->Clear();
}
wchar_t ***tab = Table->GetTable();
wprintf(L"row %d: %s %s %s %s\r\n", loops,
tab[0][0], tab[0][1], tab[0][2], tab[0][3]);
delete Table;
}
这里也是。
void Array3DTest(int loops) // 100.000 loops
{
while(loops >= 0)
{
ArrayWorker::_1D *row1 = new ArrayWorker::_1D();
ArrayWorker::_1D *row2 = new ArrayWorker::_1D();
ArrayWorker::_1D *row3 = new ArrayWorker::_1D();
ArrayWorker::_1D *row4 = new ArrayWorker::_1D();
row1->Append("nice");
row1->Append("stuff");
row2->Append("some");
row2->Append("thing");
row3->Append("another");
row3->Append("thing");
row4->Append("whatever");
row4->Append("boy");
ArrayWorker::_2D *table1 = new ArrayWorker::_2D();
ArrayWorker::_2D *table2 = new ArrayWorker::_2D();
table1->AppendRow(row1->GetArrayW());
table1->AppendRow(row2->GetArrayW());
table2->AppendRow(row3->GetArrayW());
table2->AppendRow(row4->GetArrayW());
ArrayWorker::_3D *db = new ArrayWorker::_3D();
db->AppendTable(table1->GetTable());
db->AppendTable(table2->GetTable());
wchar_t ****dat = db->GetDataBase();
wprintf(L"Table1: \n%s %s \n%s %s\n\nTable2: \n%s %s\n%s %s\n\n",
dat[0][0][0], dat[0][0][1], dat[0][1][0], dat[0][1][1],
dat[1][0][0], dat[1][0][1], dat[1][1][0], dat[1][1][1]);
delete db;
delete table2;
delete table1;
delete row4;
delete row3;
delete row2;
delete row1;
loops--;
wprintf(L"Loop left: %d\n", loops);
}
wprintf(L"DONE!\n");
}
看起来一切正常,没有内存泄漏等等。无论如何,没有使用提供的用户数组进行测试。完整的 ArrayWorker
类源代码附在这篇文章中,但是,它不包含测试函数。干杯,csrss。
关注点
在编写这个类的过程中,我可能学习了 100% 的内存管理。同时,我想感谢 CodeProject 用户 Andrew Brock,他在 CP 论坛上回答我的问题时为我澄清了一些事情。
历史
- 2011年2月11日:初始版本