将值从CSV文件存储到动态char数组 [英] Storing values from a CSV file to a dynamic char array

查看:71
本文介绍了将值从CSV文件存储到动态char数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从CSV文件中存储某些值,该文件在动态char数组中包含以下详细信息。我能够读取CSV文件,并在下面的描述中提供了相应的代码。如果有人可以告诉我,我将使用哪种方法在动态char数组中以CSV格式存储行中的某些数据,将不胜感激。谢谢!我还编写了一个子字符串函数,该函数通过放置开始索引,结束索引和源的参数来返回特定的字符串。

I am trying to store certain values from a CSV file which contains the following details in a dynamic char array. I am able to read the CSV file and have provided the code for that in the description below. I would appreciate if anyone could please let me know what approach shall I use to store certain data from a row in a CSV in a dynamic char array. Thanks! I have also written a substring function which returns a particular string by putting the parameters of starting index, end index and source.

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

CSV File :- place,name,Lat,Long,1/22/20,1/23/20,1/24/20 I want to store the dates after Long (without commas) in a dynamic char array(I cannot use vectors) Thanks!

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的指针,而推荐使用字符串向量)–原因之一是您以低效的方式处理指针的分配。您无需对输入文件进行一次遍历,而是对文件进行两次遍历,一次遍历读取行数(以分配指针),然后再次遍历每一行读取并分配存储。虽然这是一种方法,但是它是一种非常低效的方法,因为文件I / O是您可以执行的效率最低的任务之一(并且您执行两次)

Per your comment that you are still having problems dynamically allocating for the lines read from the csv file (and with the caveat, programs today should avoid the old pointer-to-pointer to char in favor of vector of strings) -- one reason is you are approaching the allocation of pointers in an inefficient manner. Instead of a single pass through your input file, you make 2-passes through your file, one to read the number of lines (to allocate pointers), and then again to read and allocate storage for each line. While that is one approach -- it is a very inefficient approach since file I/O is one of the least efficient tasks you can perform (and you do it twice)

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

Instead, simply allocate some initial number of pointers (1 or 2 or 8 is a good starting point). You then keep track of the number of allocated pointers available (say with size_t avail = 2; and the number of pointers used (say size_t used = 0;). Then as you are reading lines you check if (used == available) to know when it is time to reallocate more pointers. You can simply reallocate 2X the current number of pointers using a temporary char** pointer. You then copy the existing pointers to tmp, delete[] lines; and then assign the new block of memory containing the pointers back to lines.

另一个需要做的更改是在 main()中打开 std :: ifstream 文件流,验证它是否可以读取,然后将对打开的流的引用作为参数传递给函数,而不要传递文件名(如果无法在调用方中成功打开流,

Another change to make is to open your std::ifstream file stream in main(), validate it is open for reading, and then pass a reference to the open stream as a parameter to your function instead of passing the filename (if you can't successfully open the stream in the caller -- there is no need to make the function call to count lines)

要阅读流中的行处理分配时,您可以执行以下操作:

To read the lines from your stream handling the allocations as you go, you could do something like the following:

#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 (),它将文件名读取为程序的第一个参数,并在传递对open的引用之前在 main()中打开流流到您的读取函数将是:

Adding a short main() that takes the filename to read as the first argument to the program and opens the stream in main() before passing a reference to the open stream to your read function would be:

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

示例用法/输出

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

All lines successfully stored in allocated memory:

$ ./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

内存使用/错误检查

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

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

必须使用一个内存错误检查程序来确保您不会尝试访问内存或在分配的块的边界之外/之外进行写操作,而不会尝试读或写有条件地跳转到未初始化的值,最后确认您释放了已分配的所有内存。

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

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

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ 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)

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

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

由于您的注释与阅读第i行的问题有关,因此我已离开处理该子字符串nto分配的内存。如果您以后对此有疑问,请告诉我。如果您对上面的操作方式还有其他疑问,还请告诉我。

I have left handling the substring to you since your comment pertained to problems reading the lines into allocated memory. If you have problems with that later, just let me know. Also let me know if you have any further questions about how things were done above.

这篇关于将值从CSV文件存储到动态char数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆