65.9K
CodeProject 正在变化。 阅读更多。
Home

3D 中的动态字符串数组

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.86/5 (4投票s)

2011 年 2 月 13 日

CPOL

3分钟阅读

viewsIcon

31838

downloadIcon

171

本文介绍了创建和管理一维、二维和三维字符串数组的方法

引言

好的,先来个介绍... 我已经很久没写文章了,所以不知道我的写作技巧还在不在,总之...

这篇小文章是关于多维 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;
}

Array 1D Test

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;
}

Array 2D Test

这里也是。

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");
}

Array 3D Test

看起来一切正常,没有内存泄漏等等。无论如何,没有使用提供的用户数组进行测试。完整的 ArrayWorker 类源代码附在这篇文章中,但是,它不包含测试函数。干杯,csrss。

关注点

在编写这个类的过程中,我可能学习了 100% 的内存管理。同时,我想感谢 CodeProject 用户 Andrew Brock,他在 CP 论坛上回答我的问题时为我澄清了一些事情。

历史

  • 2011年2月11日:初始版本
© . All rights reserved.