将字符串拆分为 3d 数组
Posted
技术标签:
【中文标题】将字符串拆分为 3d 数组【英文标题】:splitting a string into a 3d array 【发布时间】:2015-10-21 10:33:04 【问题描述】:假设我们有一个字符串str
:
String str = "1,2,3,4444444444;5,6,7,8888888888;.9,10,11,1212121212;.";
定义嵌入在字符串中的三个维度d1
、d2
和d3
如下:
d1
包含由点“。”分隔的元素
d2
包含由分号“;”分隔的元素
d3
包含由逗号“,”分隔的元素
d3
是 d2
的子代,d2
是 d1
(父代)的子代。 d1 > d2 > d3
.
我们希望能够访问嵌入在字符串中的每个元素,这意味着我们应该 - 作为结果 - 一个数组,如下所示:
d[0][0][0] = "1";
d[0][0][1] = "2";
d[0][0][2] = "3";
d[0][0][3] = "4444444444";
d[0][1][0] = "5";
d[0][1][1] = "6";
d[0][1][2] = "7";
d[0][1][3] = "8888888888";
d[1][0][0] = "9";
d[1][0][1] = "10";
d[1][0][2] = "11";
d[1][0][3] = "12121212";
您可以看到d[i][j][k]
其中k = [0, 2]
是整数。
您可以看到d[i][j][k]
其中k = 3
是长整数。
我们现在希望将这些字符串转换为适当的整数类型(常规整数或长整数)。这意味着我们的最终数组应该是这样的:
d[0][0][0] = 1; // int
d[0][0][1] = 2; // int
d[0][0][2] = 3; // int
d[0][0][3] = 4444444444; // long int
d[0][1][0] = 5; // int
d[0][1][1] = 6; // int
d[0][1][2] = 7; // int
d[0][1][3] = 8888888888; // long int
d[1][0][0] = 9; // int
d[1][0][1] = 10; // int
d[1][0][2] = 11; // int
d[1][0][3] = 12121212; // long int
这个想法将在基于 c/c++ 的 arduino 微控制器上实现。
这是我的看法,不幸的是无法正常工作。
String data = "1,2,3,1445303228;4,5,6,1445303228;.7,8,9,1445303273;.";
int data_length = data.length() + 1;
if (data_length != 1)
char d0[data_length]; // couldn't do it with String, let's try char
data.toCharArray(d0, data_length);
int size1 = 32, size2 = 64; // intuitive sizes
char d1[size1][136]; // strlen("1,2,3,1445303228") * 8, 17 * 8 = 136
int i = 0;
for (char *p = strtok(d0, "."); p != NULL; p = strtok(NULL, "."))
strcpy(d1[i++], p); // d1
int d1_length = i;
char d2[d1_length][size2][17]; // strlen("1,2,3,1445303228") = 16
int d2_length[size2];
for (int i = 0; i < size2; i++) d2_length[i] = 0; // initialize array = 0
for (int i = 0; i < d1_length; i++)
int j = 0;
for (char *p = strtok(d1[i], ";"); p != NULL; p = strtok(NULL, ";"))
strcpy(d2[i][j++], p); // d2
d2_length[i] = j;
int d2_length_max = 0;
for (int i = 0; i < size2; i++)
if (d2_length[i] > d2_length_max) d2_length_max = d2_length[i];
char d3[d1_length][d2_length_max][4][10];
// d2 can have 4 d3's max, d3 can have 10 chars max
for (int i = 0; i < d1_length; i++)
for (int j = 0; j < d2_length[i]; j++)
int k = 0;
for (char *p = strtok(d2[i][j], ","); p != NULL; p = strtok(NULL, ","))
strcpy(d3[i][j][k++], p); // d3
int rows[d1_length][d2_length_max][3];
long rows_long[d1_length][d2_length_max];
for (int i = 0; i < d1_length; i++)
for (int j = 0; j < d2_length[i]; j++)
for (int k = 0; k < 3; k++)
rows[i][j][k] = atoi(d3[i][j][k]); // char array to integer
for (int i = 0; i < d1_length; i++)
for (int j = 0; j < d2_length[i]; j++)
char temp[10];
for (int k = 0; k < 10; k++) temp[k] = d3[i][j][3][k];
rows_long[i][j] = atol(temp); // char array to long
请注意,长整数实际上是一个 unix 时间戳。
【问题讨论】:
解析前知道大小吗?你有没有尝试过? 解析前知道元素个数和大小吗? @Ôrel 在解析之前我不知道大小,我们无法判断,这实际上是技巧的一部分,它取决于可以变化的字符串的大小。我在下面发布了我的尝试。 格式不一致为什么在.
之前有一个;
或者为什么在;
之前没有一个,
以及为什么要完成;.
数据?
不,不是,在您的示例中,4444444444
之后没有 ,
,那么您为什么在 5,6,7,8888888888
之后有 ;
?显示 pblm 的其他方式有 3 个 ,
用于 4 个元素,但 3 ;
用于 3 个元素
【参考方案1】:
在纯 C 中使用动态平面数组,然后分配 3D 数组 每次需要时数组大小加倍 您可以使用定义设置第一个尺寸:
#define DEFAULT_SIZE 8
正如我在评论中所说,格式很奇怪 ;.
应该是 .
并且最后的 ;.
应该被删除
我添加测试以检查下一行是否与第一行大小相同。
int main(void)
char *data = "1,2,3,1445303228;4,5,6,1445303228;.7,8,9,1445303273;.";
char *p;
int size = DEFAULT_SIZE;
int nb_element = 0;
int s1 = 0;
int s2 = 0;
int cur1 = 0;
int cur2 = 0;
int cur3 = 0;
int *d = malloc(sizeof(int) * DEFAULT_SIZE);
int ***d3;
if (!d)
return -1;
p = data;
while (*p)
if (nb_element == size)
size *= 2;
d = realloc(d, size * sizeof(int));
if (!d)
return -1;
d[nb_element++] = strtol(p, &p, 10);
switch (*p)
case ',':
cur1++;
if (s1 && cur1 > s1)
fprintf(stderr, "Too many element for %d, %d:"
" get %d, expect %d\n", cur2, cur3, cur1, s1);
return -1;
break;
case ';':
cur1++;
cur2++;
if (s1 == 0)
s1 = cur1;
cur1 = 0;
if (s2 && cur2 > s2)
fprintf(stderr, "Too many element for %d:"
" get %d, expect %d\n", cur3, cur2, s2);
return -1;
break;
case '.':
dot:
cur2++;
cur3++;
if (s2 == 0)
s2 = cur2;
cur2 = 0;
cur1 = 0;
break;
p++;
/* XXX: ack due to strange format */
if (*p == '.')
goto dot;
/*XXX! remove empty final line */
cur3--;
d3 = malloc(sizeof(int **) * cur3);
for (int i = 0; i < cur3; i++)
d3[i] = malloc(sizeof(int *) * s2);
for (int j = 0; j < s2; j++)
d3[i][j] = malloc(sizeof(int) * s1);
for (int k = 0; k < s1; k++)
d3[i][j][k] = d[i * s2 + j * s1 + k];
free(d);
for (int i = 0; i < cur3; i++)
for (int j = 0; j < s2; j++)
for (int k = 0; k < s1; k++)
fprintf(stdout, "d[%d][%d][%d] = %d\n", i,j,k, d3[i][j][k]);
return 0;
输出:
d[0][0][0] = 1
d[0][0][1] = 2
d[0][0][2] = 3
d[0][0][3] = 1445303228
d[0][1][0] = 4
d[0][1][1] = 5
d[0][1][2] = 6
d[0][1][3] = 1445303228
d[0][2][0] = 7
d[0][2][1] = 8
d[0][2][2] = 9
d[0][2][3] = 1445303273
【讨论】:
我不介意支持纯 C 解决方案,但它需要将值放入 3d 数组,而不是索引类似于 3d 数组的 1d 数组。 不得不说goto
很痛苦,但我已经按照承诺给了+1。
@JonathanMee 我知道goto
很痛苦,但我选择编写解析器以获得正确的语法并突出显示 ack 以支持格式错误。【参考方案2】:
#include <stdio.h>
#include <stdlib.h>
int c1,c2,c3;
int n=0;
char sResult[20];
void PrintLevel(int lvl)
if(n)
printf("d[%d][%d][%d] = \"%s\"\n",c3,c2,c1,sResult);
n=0;
switch(lvl)
case 3:
c3++;
c2=c1=0;
break;
case 2:
c2++;
c1=0;
break;
case 1:
c1++;
return ;
void addChar(char c)
sResult[n]=c;
n++;
sResult[n]=c=0;
/*______________________________________________________________________
*/
int main()
char buff[]= "0,1,2,4444444444;5,6,7,8888888888;.9,10,11,12121212;.";
PrintLevel(0);
char *p=buff;
char c;
int ret;
int lvl;
while(c=*p)
lvl=(c==',')?1:((c==';')?2:((c=='.')?3:0) );
if(lvl)
PrintLevel(lvl);
else
addChar(c);
p++;
PrintLevel(lvl);
【讨论】:
这不会将int
s 放入数组中。
@JonathanMee 虽然繁重的工作已经完成,但经过一些调整后我得到了它的工作,我会用我的代码更新帖子来处理这部分。【参考方案3】:
首先请注意long
太小而无法容纳:4444444444
It's max size is: 2147483647
如果使用long long
,则字符串可以由regex_token_iterator
雄辩地解析。解析函数应该接受输入string
和分隔符并返回分隔的strings
。这些输入可以在与此类似的功能中重用:
auto parse(const string& input, const string& delimiter)
const regex re("([^" + delimiter + "]+)" + delimiter + '?');
return vector<string>(sregex_token_iterator(input.begin(), input.end(), re, 1), sregex_token_iterator());
这将在分区的vector<string>
中返回input
。然后可以对该返回进行分区,直到每个分区仅包含数字字符的第三维。此时stoll
可用于将string
转换为long long
。这种划分可以像这样在嵌套循环中完成:
vector<vector<vector<long long>>> foo;
for (auto& i : parse(str, "\\."))
foo.resize(foo.size() + 1);
for (auto& j : parse(i, ";"))
foo.back().resize(foo.back().size() + 1);
for (auto& k : parse(j, ","))
foo.back().back().push_back(stoll(k));
其中一个关键方面是每个分区必须包含一个数字。这是由re
中的'+'
强制执行的,如果出现连续的分隔符,它将不匹配。 stoll
非常健壮,如果输入 string
中有空格,它会吃掉空格,但是如果输入一个只包含空格或通常不包含数字的 string
,它将引发错误。如果这是一个问题,请务必在致电 stoll
之前检查您的输入。
你可以在这里看到一个活生生的例子:http://ideone.com/4vjdBx
【讨论】:
你是对的,大约 4444444444 和更大,这些数字实际上代表一个 unix 时间戳(例如 1445303228),所以它们在长的范围内,我对虚拟字符串不好。【参考方案4】:我希望你保存了上一篇文章,这是对上一篇文章的更新,它按要求处理二维数组。希望你觉得它有用,如果有,请寄给我一张明信片:)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/*__________________________________________________________________________
*/
struct DATA
char a;
char b;
char c;
unsigned long d;
;
/*__________________________________________________________________________
allocate DATA struct, and fill it.
on error or no data, returns NULL
*/
struct DATA *ParseString(char **pstr)
int val=0;
char *p;
struct DATA *data;
if(!pstr) return NULL;
char *str=*pstr;
if(!str) return NULL;
// can be ommited
// skipping non digital chars
while(*str && !isdigit(*str))
str++;
//no digits?
if(!*str) return NULL;
p=str;
//ok at least one
data=(struct DATA*) calloc(1,sizeof(struct DATA));//calloc to initialize all to 0
for(int n=0;n<4;n++)
val=0;
while(isdigit(*p))
val*=10;
val+=(*p)-'0';
p++;
switch(n)
case 0:
data->a=val;
break;
case 1:
data->b=val;
break;
case 2:
data->c=val;
break;
case 3:
data->d=val;
break;
val=0;
if(*p!=',')
break;
p++;
*pstr=p;
return data;
/*__________________________________________________________________________
calculate the size of DATA * arra[c1][c2]
*/
void countArrBoudaries(char *s,unsigned short *c1,unsigned short *c2)
*c1=*c2=0;
unsigned short _c1=0;
while(*s)
switch(*s)
case ';':
_c1++;
break;
case '.':
if(_c1>*c1)
*c1=_c1;
_c1=0;
*c2+=1;
break;
s++;
/*__________________________________________________________________________
*/
int main(void)
struct DATA **pdata,*data;
char buff[]="1,2,3,1445303228;4,5,6,1445303228;.7,8,9,1445303273;.";
char *p=buff;
unsigned short c1,c2;
countArrBoudaries(buff,&c1,&c2);
pdata=(struct DATA**)calloc((c2*c1),sizeof(pdata));
if(pdata)
// loading
for(int i=0;i<c1;i++)
for(int j=0;j<c2;j++)
pdata[i+(j*c1)]=ParseString(&p);
// printing:
for(int i=0;i<c1;i++)
for(int j=0;j<c2;j++)
data=pdata[i+(j*c1)];
if(data)
printf("data[%u][%u]=%d,%d,%d,%u;\n",i,j,data->a,data->b,data->c,data->d);
else
printf("data[%u][%u]=NULL;\n",i,j);
// freeing memory:
for(int i=0;i<(c1*c2);i++)
free(pdata[i]);
free(pdata);
printf("\n\nDONE\n\n");
return 0;
【讨论】:
您能详细说明一下吗?你是说我可以将结构作为数组吗?我怎样才能访问所有元素?struct int x, y; long d; arr[4];
,arr[0].x = 0; arr[3].y = 1;
这样的事情怎么样,这可能吗?
它有效!我疯了!对于 3D,我们可以轻松使用 struct int x = 0, y = 0; long t = 0; arr[4][4]
;
我添加了一个示例(源代码)用于演示目的。请确保输入字符串以分号“;”结束
您甚至通过动态内存分配更进一步,这在我的情况下是必不可少的,因为我只有 8 kb 可以使用。我真的很感谢你的意见,我的朋友。我不得不使用data = (DATA *) calloc(nb, sizeof(DATA));
让它在我的芯片上运行,arduino ide 是基于 avr-gcc 的。
我现在正试图通过使用“.”来使其成为 2D。考虑到,产生一个整体的 3D 结构arr[i][j].a
以及动态内存分配,如果这可行,那将是完美的。到目前为止,我在实施它时仍然遇到一些困难,你能指出一个可以在这方面帮助我的方向吗?【参考方案5】:
让它使用主要基于@milevyo's answer 的代码。
struct ds int a = 0, b = 0, c = 0; long d = 0; ;
struct data
struct ds array[32][8];
int i, j[24];
int n = 0, d1, d2, d3; char r[11];
void get(String array)
if (array.length() + 1 != 1)
char buff[array.length() + 1];
array.toCharArray(buff, array.length() + 1);
char c; char *p = buff; int l;
while (c = *p)
l = (c == ',') ? 1 : ((c == ';') ? 2 : ((c == '.') ? 3 : 0));
if (l) a(l); else ch(c); p++;
a(l);
void a(int l)
if (n)
if (d1 == 0) array[d3][d2].a = atoi(r);
else if (d1 == 1) array[d3][d2].b = atoi(r);
else if (d1 == 2) array[d3][d2].c = atoi(r);
else if (d1 == 3) array[d3][d2].d = atol(r);
i = d3 + 1; j[d3] = d2 + 1; n = 0;
switch (l)
case 3: d3++; d2 = d1 = 0; break;
case 2: d2++; d1 = 0; break;
case 1: d1++; break;
void ch(char c) r[n] = c; n++; r[n] = c = 0;
data;
void setup ()
Serial.begin(19200);
Serial.println("..");
data.get("1,2,3,1445433855;4,5,6,1445433855;.7,8,9,1445438763;.");
for (int i = 0; i < data.i; i++)
for (int j = 0; j < data.j[i]; j++)
for (int k = 0; k < 4; k++)
if (k == 0) Serial.print(data.array[i][j].a);
else if (k == 1) Serial.print(data.array[i][j].b);
else if (k == 2) Serial.print(data.array[i][j].c);
else if (k == 3) Serial.print(data.array[i][j].d);
Serial.print("\t");
Serial.println();
Serial.println();
void loop ()
来自
1,2,3,1445433855;4,5,6,1445433855;.7,8,9,1445438763;.
到
..
1 2 3 1445433855
4 5 6 1445433855
7 8 9 1445438763
通过
..
data.array[0][0].a data.array[0][0].b data.array[0][0].c data.array[0][0].d
data.array[0][1].a data.array[0][1].b data.array[0][1].c data.array[0][1].d
data.array[1][0].a data.array[1][0].b data.array[1][0].c data.array[1][0].d
【讨论】:
以上是关于将字符串拆分为 3d 数组的主要内容,如果未能解决你的问题,请参考以下文章