【问题标题】:Objective-C to C++ reading binary file into multidimensional array of floatObjective-C 到 C++ 将二进制文件读入浮点的多维数组
【发布时间】:2020-05-09 07:56:49
【问题描述】:

我想将以下代码从目标 C 转换为 C++。

在myClass类中,我有这个属性:

float tab[dim1][dim2][dim3];

在objective-C文件中,多维数组是从二进制文件中填充的:

NSData *dataTab=[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"pathOfMyTab" ofType:@""]];
[dataTab getBytes:myClass -> tab  length:[dataTab length]];

如何将这部分翻译成 C++?

【问题讨论】:

    标签: c++ arrays objective-c binary nsdata


    【解决方案1】:

    我假设您的文件包含数组的字节表示。如果是这种情况,那么要模仿仅使用 C++ 的 Objective-C 代码的行为(使这个 C++ 成为唯一的东西是 reinterpret_cast<>,否则它只是直接的 C),您可以使用以下代码。我没有添加任何错误检查,但留下了一些您可能想要执行的 cmets。

    float tab[dim1][dim2][dim3];
    
    CFBundleRef mainBundle = CFBundleGetMainBundle();
    CFURLRef dataTabURL = CFBundleCopyResourceURL(mainBundle, CFSTR("pathOfMyTab"), NULL, NULL);
    
    CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, dataTabURL); // check for NULL return value
    CFReadStreamOpen(stream);                                              // check for errors here
    CFReadStreamRead(stream, reinterpret_cast<UInt8 *>(tab), sizeof tab);  // check that this function returns the number of bytes you were expecting (sizeof tab)
    CFReadStreamClose(stream);
    
    // we own "stream" and "dataTabURL" because we obtained these through functions
    // with "create" in the name, therefore we must relinquish ownership with CFRelease
    CFRelease(stream);
    CFRelease(dataTabURL); // ditto
    

    如果您已经在 std::string 中提供了路径,那么您可以使用以下 C++ 代码来模仿您的 Objective-C 代码的行为:

    // make sure to include this header
    #include <fstream>
    
    // ... then elsewhere in your .cpp file ...
    float tab[dim1][dim2][dim3];
    
    std::string path = "path/to/mytab"; // obtain from somewhere
    std::ifstream input(path, std::ios::binary); // check that the file was successfully opened
    input.read(reinterpret_cast<char *>(tab), sizeof tab); // check that input.gcount() is the number of bytes you expected
    

    我相信在这种情况下我们必须使用reinterpret_cast&lt;&gt;,因为文件包含数组的实际表示(假设它之前以类似的方式写入文件)。

    您可以使用混合方法,一旦您拥有包含资源路径的 CFURLRef,您就可以使用 this function 获取 URL 的文件系统表示(提供适当大小的输出缓冲区来存储结果) ,并且从那里您应该能够将其传递给std::ifstream 的构造函数之一(尽管您可能需要转换为适当的类型)。

    C++ 不支持变长数组(数组的大小必须在编译时知道)。标准库也没有提供矩阵类型,因此如果表格的尺寸在运行时发生变化,那么您将需要一种完全独立的方法来解决我的答案中的问题。您可以考虑序列化来自 Objective-C 的输出(使用例如 JSON 或其他格式),以便矩阵的维度也写入输出,从而更容易在 C++ 中解析文件。

    【讨论】:

    • 谢谢!代码的第一部分在 Objective-C 中,对吗? (你在文中提到了“直 C”)。如何在目标 C 中使用 reinterpret_cast ?似乎未知。
    • 第一部分是 C++,它只是利用基于 C 的 API (CoreFoundation) 来实现与现有 Objective-C 代码相同的结果。如果您删除 reinterpret_cast&lt;&gt; 并使用直接的 C 风格转换(例如 (UInt8 *)tab),那么代码将编译为 C 或 C++,甚至编译为 Objective-C。如果你真的想混合使用 Objective-C 和 C++,你可以使用一种称为 Objective-C++ 的混合。最简单的方法是将源文件从 .m 重命名为 .mm,然后您可以在同一个文件中混合使用 Objective-C 和 C++ 语法。
    • 好的,我意识到 CoreFoundation API 只在 MacOs 上。所以如果我在另一个环境中编程(我目前正在使用Linux),我不能使用这部分。我找到了一些包装器,但这似乎有点过头了。
    • 第二部分效果很好,谢谢!事实上,我知道文件的路径。由于我的问题是关于通用 C++ 代码的,您可以编辑您的答案以突出显示这部分,如果可能的话,更多地解释 reinterpret_cast 的目的,这对我来说仍然很模糊。
    • 啊,我没有意识到您正在尝试在非 macOS 环境中使用 C++。有 CoreFoundation 实现,它们对于你想要做的事情来说太过分了,这可以用普通的旧 C++ 来实现。通常,C++ 对类型非常严格,并试图阻止您尝试将一种类型用作另一种类型(例如,尝试将long * 用作int *)。 input.read() 函数从文件中读取并写入char * 缓冲区,但是,我们希望它直接写入您的float 数组,因此我们必须将float (*)[][] 类型“重新解释”为char *
    【解决方案2】:

    看看fstreamfreadread,都是读取二进制文件,选择适合的方法。

    【讨论】:

      【解决方案3】:

      在我看来,最简单和最快的方法是使用 memcpy() 将 NSData 的字节复制到与源数组具有相同结构(维度)的目标数组中。参见,例如:

      https://github.com/Voldemarus/MultiDimensionalArrayDemo/tree/master

      #import "DemoClass.h"
      
      #define DIM1    3
      #define DIM2    4
      #define DIM3    2
      
      @interface DemoClass() {
          int src[DIM1][DIM2][DIM3];  // source (initial) array
      
          int dst[DIM1][DIM2][DIM3];  // destination array
      }
      @end
      
      @implementation DemoClass
      
      - (instancetype) init
      {
          if (self = [super init]) {
              for (int i = 0; i < DIM1; i++) {
                  for (int j = 0; j < DIM2; j++) {
                      for (int k = 0; k < DIM3; k++) {
                          int value = i*100 + j*10 + k;
                          src[i][j][k] = value;
                      }
                  }
              }
          }
          return self;
      }
      
      int getIntFromArray(int *array, int i, int j, int k) {
          int offset = j*DIM3 + i*DIM2*DIM3;
          return array[offset];
      }
      
      void putIntToArray(int *array, int i, int j, int k, int value) {
          int offset = j*DIM3 + i*DIM2*DIM3;
          array[offset] = value;
      }
      
      - (void) run
      {
          // Step 1. Save array into NSData
          NSInteger s = sizeof(int)*DIM1*DIM2*DIM3;
          NSData *data = [[NSData alloc] initWithBytes:src length:s];
          NSAssert(data, @"NSData should be created");
          //Step2 - Create new array
          int *bytes = (int *)[data bytes];
          memcpy(dst,bytes,s);
          // Step 3. Compare src and dst
          for (int i = 0; i < DIM1; i++) {
              for (int j = 0; j < DIM2; j++) {
                  for (int k = 0; k < DIM3; k++) {
                      int template = i*100 + j*10 + k;
                      int s = src[i][j][k];
                      int d = dst[i][j][k];
       //               NSLog(@"i %d j %d k %d -->s = %d  d = %d",i,j,k,s,d);
                      NSAssert(s == template, @"Source array should have value from template");
                      NSAssert(d == s, @"Destination array should be identical to the source");
                  }
              }
          }
      
      }
      
      @end
      

      【讨论】:

      • 我想你的答案提到 memcpy() 将数组复制到一个可以在 C++ 中读取的文件中?您能否添加代码以将此数组写入文件(在 Objective-C 中)?谢谢
      【解决方案4】:

      float tab[dim1][dim2][dim3] 看起来像一个三维数组。标准实现是三个嵌套的FOR 循环。

      所以你的 C++ 实现可以如下所示:

      • 从某处读取dim1dim2dim3,通常是文件中的第一个值(例如12个字节,每个数字4个字节)
      • 在三个嵌套的 FOR 循环中读取文件的其余部分

      类似:

          for (size_t i = 0; i < dim1; ++i) 
             for (size_t j = 0; j < dim2; ++j)
               for (size_t k = 0; k < dim3; ++k)
                 tab[i][j][k] = read_float_value(inputFile);
      

      在 Objective-C 中,您可以用类似的方式编写文件。

      以下是一些帮助您入门的示例:

      【讨论】:

      • 确实是一个三维数组。但是我不知道我在Objective-C中用NSData读取的二进制文件是否可以直接用C++读取。你怎么看?
      猜你喜欢
      • 1970-01-01
      • 2021-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      • 1970-01-01
      • 2012-10-13
      • 1970-01-01
      相关资源
      最近更新 更多