c++ - 将 CSV 文件中的值存储到动态字符数组

标签 c++ arrays

我正在尝试存储 CSV 文件中的某些值,该文件在动态字符数组中包含以下详细信息。我能够读取 CSV 文件,并在下面的描述中提供了该文件的代码。如果有人能让我知道我应该使用什么方法将 CSV 中的行中的某些数据存储在动态字符数组中,我将不胜感激。谢谢!我还编写了一个子字符串函数,它通过输入起始索引、结束索引和源的参数来返回特定的字符串。

CSV 文件:- 地点,名称,纬度,经度,1/22/20,1/23/20,1/24/20 我想将 Long (不带逗号)之后的日期存储在动态字符数组中(我不能使用 vector )谢谢!

char* substring(char* source, int startIndex, int endIndex)
{
    int size = endIndex - startIndex + 1;
    char* s = new char[size+1];
    strncpy(s, source + startIndex, size); 
    s[size]  = '\0'; //make it null-terminated
    return s;
} 

char** readCSV(const char* csvFileName, int& csvLineCount)
{
    ifstream fin(csvFileName);
    if (!fin)
    {
        return nullptr;
    }
    csvLineCount = 0;
    char line[1024];

最佳答案

根据您的评论,您仍然在动态分配从 csv 文件读取的行时遇到问题(并且需要注意的是,今天的程序应该避免使用旧的指向 char 的指针,而是使用字符串 vector )--原因之一是您正在以低效的方式分配指针。您不是单次遍历输入文件,而是两次遍历文件,一次读取行数(以分配指针),然后再次读取并为每一行分配存储空间。虽然这是一种方法,但它是一种非常低效的方法,因为文件 I/O 是您可以执行的效率最低的任务之一(而且您需要执行两次)

相反,只需分配一些初始数量的指针(1 或 2 或 8 是一个很好的起点)。然后,您可以跟踪可用的已分配指针数量(例如 size_tavail = 2;)和已使用的指针数量(例如size_tused = 0;)。您正在阅读检查 if (used == available) 的行,以了解何时需要重新分配更多指针。您可以简单地使用临时 char** 重新分配当前指针数量的 2 倍 指针。然后将现有指针复制到 tmpdelete[]lines;,然后将包含指针的新内存块分配回

要进行的另一个更改是在 main() 中打开您的 std::ifstream 文件流,验证它是否打开以供读取,然后传递对打开流作为函数的参数,而不是传递文件名(如果您无法在调用者中成功打开流——则无需调用函数来计数行数)

要从流中读取处理分配的行,您可以执行如下操作:

#include <iostream>
#include <fstream>
#include <cstring>

#define MAXC 1024

char **readcsv (std::ifstream& fin, size_t& csvLineCount)
{
    size_t avail = 2, used = 0;                     /* allocated/used counters */
    char line[MAXC], **lines = new char*[avail];    /* line and lines */

    while (fin.getline (line, MAXC)) {              /* loop reading each line */
        size_t len;                                 /* for line length */
        if (used == avail) {                        /* all pointers used? */
            char **tmp = new char *[2 * avail];     /* allocate twice as many */
            memcpy (tmp, lines, used * sizeof *lines);  /* copy lines to new tmp */
            delete[] lines;                         /* free existing pionters */
            lines = tmp;                            /* set lines to new block */
            avail *= 2;                             /* update ptrs available */
        }
        lines[used] = new char[(len = strlen(line)) + 1];   /* alloc for lines[used] */
        memcpy (lines[used++], line, len + 1);      /* copy line to lines[used] */
    }
    csvLineCount = used;                            /* update csvLineCount to used */

    return lines;       /* return lines */
}

添加一个简短的 main() ,它将要读取的文件名作为程序的第一个参数,并在传递对文件的引用之前在 main() 中打开流打开您的读取功能的流将是:

int main (int argc, char **argv) {

    if (argc < 2) { /* validate 1 argument given for filename */
        std::cerr << "error: insufficient input.\n"
                     "usage: " << argv[0] << " filename.\n";
        return 1;
    }

    char **lines = nullptr;         /* pointer-to-pointer to char */
    size_t nlines = 0;              /* line counter */
    std::ifstream f (argv[1]);      /* file stream */

    if (!f.is_open()) {     /* validate file open for reading */
        std::cerr << "error: file open failed '" << argv[1] << "'.\n";
        return 1;
    }

    if (!(lines = readcsv (f, nlines))) {   /* call line read function/validate */
        std::cerr << "error: readcsv() failed.\n";
        return 1;
    }

    for (size_t i = 0; i < nlines; i++) {   /* loop outputting lines, freeing memory */
        std::cout << lines[i] << '\n';
        delete[] lines[i];                  /* free lines */
    }
    delete[] lines;                         /* free pointers */
}

输入文件示例

$ cat dat/latlon.csv
place1,name1,Lat1,Long1,1/22/20,1/23/20,1/24/20
place2,name2,Lat2,Long2,1/22/20,1/23/20,1/24/20
place3,name3,Lat3,Long3,1/22/20,1/23/20,1/24/20
place4,name4,Lat4,Long4,1/22/20,1/23/20,1/24/20

示例使用/输出

所有行都成功存储在分配的内存中:

$ ./bin/read_alloc_csv_lines dat/latlon.csv
place1,name1,Lat1,Long1,1/22/20,1/23/20,1/24/20
place2,name2,Lat2,Long2,1/22/20,1/23/20,1/24/20
place3,name3,Lat3,Long3,1/22/20,1/23/20,1/24/20
place4,name4,Lat4,Long4,1/22/20,1/23/20,1/24/20

内存使用/错误检查

在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您都有两个责任:(1) 始终保留指向起始地址的指针内存块,因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或在分配的 block 的范围之外进行写入,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于 Linux,valgrind 是正常选择。每个平台都有类似的内存检查器。它们使用起来都很简单,只需通过它运行您的程序即可。

$ valgrind ./bin/read_alloc_csv_lines dat/latlon.csv
==8108== Memcheck, a memory error detector
==8108== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8108== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8108== Command: ./bin/read_alloc_csv_lines dat/latlon.csv
==8108==
place1,name1,Lat1,Long1,1/22/20,1/23/20,1/24/20
place2,name2,Lat2,Long2,1/22/20,1/23/20,1/24/20
place3,name3,Lat3,Long3,1/22/20,1/23/20,1/24/20
place4,name4,Lat4,Long4,1/22/20,1/23/20,1/24/20
==8108==
==8108== HEAP SUMMARY:
==8108==     in use at exit: 0 bytes in 0 blocks
==8108==   total heap usage: 10 allocs, 10 frees, 82,712 bytes allocated
==8108==
==8108== All heap blocks were freed -- no leaks are possible
==8108==
==8108== For counts of detected and suppressed errors, rerun with: -v
==8108== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放分配的所有内存并且不存在内存错误。

由于您的评论涉及将行读入分配的内存的问题,因此我已将子字符串的处理留给您。如果您稍后遇到问题,请告诉我。如果您对上述操作还有任何疑问,也请告诉我。

关于c++ - 将 CSV 文件中的值存储到动态字符数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61356961/

相关文章:

PHP 数组,将数组项的计数递归附加到数组

c++ - 当为将放入数组的字符串列表动态分配内存时,是否可以在程序的后面添加到该列表?

c - 将矩阵作为参数传递

c - 在 C 中传递二维指针数组

c++ - 在 *NIX 上定位 C/C++ 库中要链接的哪些方法

c++ - 运算符 << 中的未格式化输出

C++ 将 char 添加到 char[1024]

c++ - 获取可变参数模板类的第 N 个参数的最简单方法?

c++ - 在 Qt 中呈现图像列表的最佳方式是什么?

javascript - 理解 javascript 对象与 map