如何在C中编辑.csv文件

时间:2018-02-14 18:15:05

标签: c csv

我是编程新手,我在C项目中需要帮助。我必须搜索一个城市,确认它存在于第一个文件(city.csv)中,并从那里获取其ID。然后我必须将该id与第二个文件(meteo.csv)中的相应ID匹配,然后编辑其天气信息,即第二个文件中的天气信息。但是,我不知道如何从第一个文件中获取城市ID,然后在获取所有新的天气信息后如何编辑第二个文件。这是代码:

void addInfo() {
FILE * fp;
char id_city[100];    
char city[100];    
char humidity[100];    
char temp_max[100];    
char temp_min[100];
char pressure[100];
char date[100];   

printf("Name of the city: ");
scanf("%s", city);


// I think it's here that I have to write the code for take the city's id from the first file

if (id_city != NULL) {
    printf("Maximun temperature: ");
    scanf("%s", temp_max);
    printf("Minimun temperature: ");
    scanf("%s", temp_min);
    printf("Humidity: ");
    scanf("%s", humidity);
    printf("Pressure: ");
    scanf("%s", pressure);
    printf("Date, in the format YYYY-MM-DD: ");
    scanf("%s", date);

    fp = fopen ("meteo.csv", "a");
    fprintf(fp, "%s, %s, %s, %s, %s \n", temp_max, temp_min, humidity, pressure, date); //I think there's something wrong here too...

    fclose(fp);
    printf("Information edited successfully");

}

文件city.csv有152行和4列:

(id_city,city,county,district)

,例如

(56,Lisbon,Lisbon,Lisbon)

文件meteo.csv有152行和7列:

(id_meteo_city,id_city,temp_max,temp_min,humidity,pressure,date) 

,例如

(56,56,14,5,62,1025,2018-02-12)

1 个答案:

答案 0 :(得分:0)

我要做的第一件事就是将数据封装在一个struct中 更容易将CSV文件的一行映射到表示一行的对象中。

如果文件city.csvmeteo.csv都有不同的列,我会创建一个 每个文件不同struct。如果两个文件具有相同的列,则可以 使用struct。我假设两个文件都不同,city有 格式meteo_id,city_id,name

typedef struct city_t {
    int meteo_id;
    int city_id;
    char name[100];  // no city should have
                     // longer than 100 chars
} city_t;

typedef struct meteo_t {
    int meteo_id;
    int city_id;
    int tempt_max;
    int tempt_mix;
    double humidity;
    double preassure;
    char date[11];
} meteo_t;

我们假设这两个文件都格式正确,否则你必须这样做 编写检查错误并处理错误的代码,这将是下一步 在练习中,所以我将只编写带有基本错误的基本版本 识别。

#include <stdio.h>
#include <string.h>
#include <errno.h>

// takes 2 params, the filename and a pointer
// to size_t where the number of cities is stored
city_t *read_cities(const char *filename, size_t *len)
{
    if(filename == NULL || len == NULL)
        return NULL;

    FILE *fp = fopen(filename, "r");
    if(fp == NULL)
    {
        fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
        return NULL;
    }

    city_t *arr = NULL, *tmp;
    *len = 0;

    // assuming that no line will be longer than 1023 chars long
    char line[1024];

    while(fgets(line, sizeof line, fp))
    {
        tmp = realloc(arr, (*len + 1) * sizeof *arr);
        if(tmp == NULL)
        {
            fprintf(stderr, "could not parse the whole file %s\n", filename);
            // returning all parsed cities so far

            if(*len == 0)
            {
                free(arr);
                arr = NULL;
            }

            return arr;
        }

        arr = tmp;

        // %99[^\n] is to read up to 99 characters until the end of the line
        if(sscanf(line, "%d,%d,%99[^\n]", &(arr[*len].meteo_id),
                    &(arr[*len].city_id), arr[*len].name) != 3)
        {
            fprintf(stderr, "Invalid line format (skipping line):\n%s\n", line);
            // skip this line, and decrement *len
            (*len)--;
            continue;
        }

        // incrementing only when parsing of line was OK
        (*len)++;
    }

    fclose(fp);

    // file is empty or
    // all lines have wrong format
    if(*len == 0)
    {
        free(arr);
        arr = NULL;
    }


    return arr;
}

void print_cities(city_t *cities, size_t len, FILE *fp)
{
    if(cities == NULL || fp == NULL)
        return;

    for(size_t i = 0; i < len; ++i)
        fprintf(fp, "%d,%d,%s\n", cities[i].meteo_id, cities[i].citiy_id,
                cities[i].name);
}

现在我已经为文件citiy.csv编写了读写函数 格式meteo_id;city_id;nameprint_cities允许您打印CSV 屏幕上的内容(将stdout作为最后一个参数传递)或文件 (传递FILE对象作为最后一个参数。)

您可以将这些功能用作读写meteo.csv的模板 想法是一样的。

您可以按如下方式使用这些功能:

int main(void)
{
    size_t cities_len;
    city_t *cities = read_cities("city.csv", &cities_len);

    // error
    if(cities == NULL)
        return 1;

    do_something_with_cities(cities, cities_len);

    // update csv
    FILE *fp = fopen("city.csv", "w");

    if(fp == NULL)
    {
        fprintf(stderr, "Could not open city.csv for reading: %s\n",
                strerror(errno));

        free(cities);
        return 1;
    }

    print_cities(cities, cities_len, fp);

    fclose(fp);
    free(cities);
    return 0;
}

现在练习:编写一个解析meteo.csv的类似函数(使用 我作为模板的功能应该不那么困难)并解析这两个文件。现在 你已经将它们存储在内存中,操作数据很容易(插入, 更新,删除)。然后像我在示例中所做的那样编写文件,就是这样。

最后一个提示:如何搜索城市:

// returns the index in the array or -1 on error or when not found
int search_for_city_by_name(city_t *cities, size_t len, const char *name)
{
    if(cities == NULL || name == NULL)
        return -1;

    for(size_t i = 0; i < len; ++i)
        if(strcmp(name, cities[i].name) == 0)
            return i;

    // not found
    return -1;
}

现在我已经给了你几乎所有的任务部分,你所要做的就是 将它们粘在一起并为meteo.csv文件编写相同的函数。