【问题标题】:Convert string to double in C在C中将字符串转换为双精度
【发布时间】:2022-01-20 10:51:18
【问题描述】:

我必须从 txt 文件中读取这些行:
1 334.5909245845 161.7809319139
2 397.6446634067 262.8165330708
3 503.8741827107 172.8741151168
4 444.0479403502 384.6491809647
5 311.6137146746 2.0091699828
6 662.8551011379 549.2301263653
7 40.0979030612 187.2375430791

从这里我必须提取每行的第二个和第三个值,因为它们是我的城市的坐标。 我的一段代码如下(我将只显示程序必须读取值的部分):

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>


struct city {
    double x;
    double y;    
};

int main(int argc, const char *argv[])
{

    FILE *f;
    f = fopen(argv[1], "r");
    if (f == NULL)
    {
        printf("cannot read file or inexistant file.\n");
        return 1;
    }
    char lines[2000][150];
    int i = 0;
    int j = 0;
    int c = 0;
    // read file's lines
    while (c != EOF)
    {
        while ((c = getc(f)) != EOF && c != '\n')
        {
            lines[i][j] = c;
            j++;
        }
        lines[i][j] = '\n';
        i++;
        j = 0;
    }
    strcpy(lines[i - 2], "\n");


    struct city * cities = malloc(sizeof(double) * 10 + 1);
    if (cities == NULL)
    {
        printf("cannot allocate memory");
        return 1;
    }

    int counter = 0;
    for (int y = 0; strcmp(lines[y], "\n") != 0; y++)
    {
        // don't need this check
        if (isdigit(lines[y][0]))
        {
            char * tok;
            struct city *new_city = malloc(sizeof(struct city));
            if (new_city == NULL)
            {
                printf("cannot allocate memory");
                free(cities);
                return 1;
            }
            //read first number, not used
            tok = strtok(lines[y], " ");

            //read coordinate x
            tok = strtok(NULL, " ");
            printf("tok1: %s\n", tok);
            new_city -> x = atof(tok);

            //read coordinate y
            tok = strtok(NULL, " ");

            printf("tok2: %s\n", tok);
            new_city -> y = atof(tok);
            printf("inserted: %lf\n", new_city -> y);
            cities[counter] = *new_city;
            counter++;
        }
    }
   fclose(f);

我只需打开文件,逐字符读取所有行,然后使用 strtok() 获取写入的坐标(每行的第二个和第三个数字)。问题是我必须将它们存储到我的城市结构的 x 和 y 中,并且正如我在这里所读到的,必须使用 atof(),但它近似于数字,然后它返回分段错误,正如我在这里打印的那样(插入的是城市->y 是近似的但它是错误的,而 tok1 和 tok2 是从文件中读取的两个正确字符串):

tok1: 334.5909245845
tok2: 161.7809319139

inserted: 161.780932
tok1: 397.6446634067
tok2: 262.8165330708

inserted: 262.816533
tok1: 503.8741827107
tok2: 172.8741151168

inserted: 172.874115
tok1: 444.0479403502
tok2: 384.6491809647

zsh: segmentation fault  ./Travelling_salesman_problem ch130.tsp

您可以看到比较插入的值和 tok2,插入的值是近似的,然后代码中断。有一种方法不需要更改代码,但只有 atof() 函数具有精确值(因为其余代码有效)?

【问题讨论】:

  • struct city * cities = malloc(sizeof(double) * 10 + 1); 这为 double 类型的 10 个元素分配内存(加上 1 个无用的额外字节)。但在我看来,你想要 N 个 struct city 类型的元素。
  • @ElenaFranchini 如果您来这里寻求帮助,我们提供建议,请听取我们的建议!我们确实知道我们在说什么,我们给您的这些建议是有效的。你对\n 所做的事情,yano 所问的,不可能是对的。您分配的 10 个 double 绝对不够:您的示例数据将需要 14 个。如果对这些问题的狭隘修复没有解决所有问题,那是因为您的代码有多个问题。但在代码可以工作之前,这些东西肯定也需要修复。
  • 如果您计划分配 10 个城市结构,这是错误的做法:struct city * cities = malloc(sizeof(double) * 10 + 1); — 您应该使用 sizeof(cities[0]) 而不是 sizeof(double),而 + 1 是巫毒编程.您的崩溃来自于尝试将 7 个城市(14 个双打)存储到一个只能容纳 5 个城市(10 个双打)的数组中。既然知道malloc(),也就知道realloc(),可以安排增量分配数组,每次加倍行数。你需要两个计数器——分配的城市数量和使用的城市数量。
  • 语法错误和运行时错误之间有很大的不同,正如您在段错误中看到的那样。您是绝对正确的,编译器并不关心您是否使用'\n''\0' 或任何其他字符“终止”您的字符串。但是strcpystrcmp 关心。这些函数使用 strings,在 C 中被定义为一个或多个以 '\0' 结尾的 chars 的序列。向str* 函数提供格式不正确的字符串会调用undefined behavior,这可能表现为段错误。
  • 除了已经提到的问题之外,代码比它需要的要复杂得多。您不需要将整个文件读入二维lines 数组——您可以一次读取一行。您可以通过调用fgets 来读取文本行,而不是一次读取一个字符。您不需要每次都分配new_city。我并不是说这些事情中的任何一个都是你的问题的直接原因,但是通过比它们必须的更复杂,它们增加了出现问题的几率,并且它们使得更难发现真正的问题在哪里。

标签: c string type-conversion stdin long-integer


【解决方案1】:

问题太多了。

不清楚为什么所有行都先读入char lines[][];,然后再解析。更有意义的是读取 1 行,将其解析为数据结构,然后读取下一行,等等。然后只需要一个行缓冲区。

但假设需要先读取整个文件,一些代码来修复输入:

// Avoid naked magic numbers
//  char lines[2000][150];
#define LINES_N 2000
#define LINE_SIZE 150
char lines[LINES_N][LINE_SIZE];

int i = 0;  // Maybe line_count instead?
int j = 0;  // Maybe ch_index instead?
int c = 0;  // Good use of int here.


// Append a '\0' at the end to form a _string_.
// Use 1 while loop
// Do not iterate to far, add limits tests

// read file's lines
while (i < LINES_N && (c = getc(f)) != EOF) {
  if (j < LINE_SIZE) {
    lines[i][j] = c;
    j++;
  } else {
    fprintf(stderr, "Line %d too long\n", i);
  }    
  if (c == '\n') { 
    lines[i][j] = '\0';
    i++;
    j = 0;
  }
}

// Might as well close the file now, not later.
fclose(f);  // and remove later one.

// lines[i - 2] is very bad if i < 2
//strcpy(lines[i - 2], "\n");
// I think all you want here is 
// strcpy(lines[i], "\n");
// Since you have `i`, later iterate to `i`,
//   no need for strcpy(lines[i], "\n")
//
// for (int y = 0; strcmp(lines[y], "\n") != 0; y++)
for (int y = 0; y < i; y++)

如果有帮助,稍后我会查看其余部分。

【讨论】:

    猜你喜欢
    • 2013-10-10
    • 2015-04-10
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    相关资源
    最近更新 更多