将字符串拆分为 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;.";

定义嵌入在字符串中的三个维度d1d2d3如下:

d1 包含由点“。”分隔的元素 d2 包含由分号“;”分隔的元素 d3 包含由逗号“,”分隔的元素

d3d2 的子代,d2d1(父代)的子代。 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);


【讨论】:

这不会将ints 放入数组中。 @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&lt;string&gt; 中返回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 数组的主要内容,如果未能解决你的问题,请参考以下文章

将数组拆分为字符串[关闭]

将字符串拆分为字符串数组

将字符串拆分为对象数组

将数组拆分为字符串

将逗号分隔的字符串拆分为数组?

Android将字符串拆分为数组[重复]