简体   繁体   English

如何从文件的一行中读取多个浮点数

[英]How do I read multiple floats from one line of a file

I have the following code which reads from a given input file into and then into struct I have made.我有以下代码,它从给定的输入文件中读取,然后读取到我制作的结构中。

    OFFFile ReadOFFFile(OFFFile fileData, FILE* srcFile)
{
    int nvert, nfaces;
    fscanf(srcFile, "%s\n");
    fscanf(srcFile, "%d %d %s\n", &nvert, &nfaces);
    fileData.nvert = nvert;
    fileData.nfaces = nfaces;
    
    fileData.vertices = (int *) malloc(fileData.nvert * sizeof(float));
    fileData.triFaces = (int *) malloc(fileData.nfaces * sizeof(int));

    // Print to check correct size was allocated
    printf("%d\n", (fileData.nvert * sizeof(float)));
    printf("%d\n", (fileData.nfaces * sizeof(int)));

    int i;
    float ftemp1, ftemp2, ftemp3;
    int itemp1, itemp2, itemp3;

    fscanf(srcFile, "%f", &ftemp1);
    printf("%lf", ftemp1);
    fscanf(srcFile, "%f", &ftemp2);
//    fscanf(srcFile, " %lf", &ftemp3);

/*    for (i = 0; i < nvert; ++i)
    {
        fscanf(srcFile, "%f %f %f\n", &ftemp1, &ftemp2, &ftemp3);
        fileData.vertices[i].x = ftemp1;
        fileData.vertices[i].y = ftemp2;
        fileData.vertices[i].z = ftemp3;
    }
*/
    return(fileData);
}

The problem I am having is with the whole last section that is currently in quotes (The 2 fscanf lines above it are me attempting to test).我遇到的问题是当前在引号中的整个最后一部分(上面的 2 fscanf 行是我试图测试的)。 If I have just one float being read it works fine, but when I add the second or third the whole function fails to even run, although it still compiles.如果我只读取一个浮点数,它工作正常,但是当我添加第二个或第三个时,整个 function 甚至无法运行,尽管它仍然可以编译。 I believe it to be caused by the negative sign in the input, but I don't know how I can fix it.我认为这是由输入中的负号引起的,但我不知道如何解决它。

The data is in the form数据在表格中

OFF
4000 7000 0
0.8267261981964111 -1.8508968353271484 0.6781123280525208
0.7865174412727356 -1.8490413427352905 0.7289819121360779

With the floats continuing on for 4000 lines (hence for loop).浮点数持续 4000 行(因此为循环)。 These are the structs I have made这些是我制作的结构

typedef struct
{
    float x;
    float y;
    float z;
} Point3D;

typedef struct
{
    int face1;
    int face2;
    int face3;
} triFace;

typedef struct
{
    int nvert;
    int nfaces;
    Point3D *vertices;
    triFace *triFaces;
} OFFFile;

Text dump of another file with a lot less lines, also does not work in the for loop.另一个文件的文本转储行少得多,在 for 循环中也不起作用。 Only using this for testing.仅用于测试。 https://justpaste.it/9ohcc https://justpaste.it/9ohcc

The line:该行:

fscanf(srcFile, "%s\n");

is invoking undefined behavior.正在调用未定义的行为。 The compiler should warn you about that.编译器应该警告你。 Once you've invoked UB, there's no point in speculating further about what is happening.一旦您调用了 UB,就没有必要进一步推测正在发生的事情。

It's not clear to me what you intended that line to do, but if you use %s in a scanf, you need to give it a valid place to write data.我不清楚你打算用那条线做什么,但如果你在 scanf 中使用%s ,你需要给它一个有效的地方来写入数据。 You should always check the value returned by scanf to ensure that you have actually read some data, and you should never use "%s" without a width modifier.您应该始终检查 scanf 返回的值以确保您确实读取了一些数据,并且您永远不应该使用没有宽度修饰符"%s" Perhaps you want something like:也许你想要类似的东西:

char buf[256];
if( fscanf(srcFile, "%255s ", buf) == 1 ){
        /* Do something with the string in buf */
}

From your comment, it seems that you are intending to use that scanf to skip a line.从您的评论来看,您似乎打算使用该 scanf 来跳过一行。 I strongly recommend using a while(fgetc) loop instead of scanf to do that.我强烈建议使用while(fgetc)循环而不是 scanf 来执行此操作。 If you do want to use scanf, you could try something like fscanf(srcFile, "%*s\n") , but beware that it will stop at the first whitespace, and not necessarily consume an entire line.如果您确实想使用 scanf,您可以尝试类似fscanf(srcFile, "%*s\n") ,但要注意它会在第一个空格处停止,并且不一定会占用整行。 You could also do fscanf(srcFile, "%*[^\n]%*c");你也可以做fscanf(srcFile, "%*[^\n]%*c"); to consume the line, but you are really better off using a fgetc in a while loop.消耗这条线,但你最好在while循环中使用fgetc

Addressing title question:解决标题问题:

"How do I read multiple floats from one line of a file" “如何从文件的一行中读取多个浮点数”

...with suggestions for a non-scanf() approach. ...与非 scanf() 方法的建议。

Assuming the file is opened, (and a file pointer) fp is obtained ), the first two lines are already handled, and values into ints, say the lines value is converted to int lines;假设打开文件,(并获得文件指针) fp ),前两行已经处理,并将值转换为 int,即值转换为int lines;

And given your struct definition (modified to use double to accommodate type compatibility in code below):并给定您的结构定义(修改为使用double以适应下面代码中的类型兼容性):

typedef struct
{
    double x;
    double y;
    double z;
} Point3D; 

In a function somewhere here is one way to parse the contents of each data line into the 3 respective struct values using fgets() , strtok() and strtod() :在 function 中的某处是使用fgets()strtok()strtod()将每个数据行的内容解析为 3 个相应结构值的一种方法:

char delim[] = " \n";
char *tok = NULL;
char newLine[100] = {0};
Point3D *point = calloc(lines, sizeof(*point));
if(point)
{
    int i = 0;
    while(fgets(newLine, sizeof newLine, fp))
    {
        tok = strtok(newLine, delim);
        if(tok)
        {
            if(parseDbl(tok, &point[i].x))
            {
                tok = strtok(NULL, delim);
                if(tok)
                {
                    if(parseDbl(tok, &point[i].y))
                    {
                        tok = strtok(NULL, delim);
                        if(tok)
                        {
                            if(!parseDbl(tok, &point[i].z))
                            {
                                ;//handle error
                            }else ;//continue
                        }else ;//handle error
                    }else ;//handle error
                }else ;//handle error
            }else ;//handle error
        }else ;//handle error 
        i++;//increment for next read
    }//end of while
}else ;//handle error  

Where parseDbl is defined as:其中parseDbl定义为:

bool parseDbl(const char *str, double *val)
{
    char *temp = NULL;
    bool rc = true;
    errno = 0;
    *val = strtod(str, &temp);

    if (temp == str)
        rc = false;

    return rc;
}       

Your main problem is the first line in the readOFFFile function:您的主要问题是readOFFFile function 中的第一行:

fscanf(srcFile, "%s\n");

This tries to read a string (presumably the string OFF on the first line of the file), but you don't give fscanf any place to store the string, so it crashes.这会尝试读取一个字符串(可能是文件第一行上的字符串OFF ),但是您没有给fscanf任何存储字符串的地方,所以它崩溃了。 (As an aside, your compiler really should have warned you about this problem. If it didn't, it's old-fashioned, and there are lots of easy mistakes that it's probably not going to warn you about, and learning C is going to be much harder than it ought to be. Or perhaps you just need to find an option flag or checkbox to enable more warnings.) (顺便说一句,你的编译器真的应该警告你这个问题。如果没有,它就是过时的,并且有很多简单的错误可能不会警告你,学习 C 将会比应有的困难得多。或者也许你只需要找到一个选项标志或复选框来启用更多警告。)

You can tell fscanf to read and discard something, without storing it anywhere, using the * modifier.您可以使用*修饰符告诉fscanf读取和丢弃某些内容,而不将其存储在任何地方。 Here's a modified version of your program, that works for me.这是您的程序的修改版本,对我有用。

void ReadOFFFile(OFFFile *fileData, FILE* srcFile)
{
    fscanf(srcFile, "%*s");
    if(fscanf(srcFile, "%d %d %*s", &fileData->nvert, &fileData->nfaces) != 2) {
        exit(1);
    }
    
    fileData->vertices = malloc(fileData->nvert * sizeof(Point3D));
    fileData->triFaces = malloc(fileData->nfaces * sizeof(triFace));

    int i;

    for (i = 0; i < fileData->nvert; ++i)
    {
        if(fscanf(srcFile, "%f %f %f", &fileData->vertices[i].x,
                                       &fileData->vertices[i].y,
                                       &fileData->vertices[i].z) != 3) {
            exit(1);
        }
    }
}

I have made a few other changes.我做了一些其他的改变。 The other fscanf call, that reads three values but only stores two, also needs a * modifier.另一个读取三个值但只存储两个值的fscanf调用也需要一个*修饰符。 I check the return value of fscanf to catch errors (via a crude exit) if the input is not as expected.如果输入不符合预期,我检查fscanf的返回值以捕获错误(通过粗略退出)。 I got rid of the \n characters in the fscanf calls, since they're not necessary, and potentially misleading.我去掉了fscanf调用中的\n字符,因为它们不是必需的,并且可能会产生误导。 I got rid of some unnecessary temporary variables, and I had the readOFFFile function accept a pointer to an OFFFile structure to fill in, rather than passing and returning it.我去掉了一些不必要的临时变量,我让readOFFFile function 接受一个指向OFFFile结构的指针来填充,而不是传递和返回它。

Here is the main program I tested it with:这是我测试它的主要程序:

int main()
{
    OFFFile fd;
    FILE *fp = fopen("dat", "r");
    ReadOFFFile(&fd, fp);
    for (int i = 0; i < fd.nvert; ++i)
        printf("%f %f %f\n", fd.vertices[i].x, fd.vertices[i].y, fd.vertices[i].z);
}

This is still an incomplete program: there are several more places where you need to check for errors (opening the file, calling malloc , etc.), and when you do detect an error, you need to at least print a useful error message before exiting or whatever.这仍然是一个不完整的程序:还有几个地方需要检查错误(打开文件,调用malloc等),当您确实检测到错误时,您至少需要在之前打印一个有用的错误消息退出或其他。

One more thing.还有一件事。 As I mentioned, those \n characters you had in the fscanf format strings were unnecessary and misleading.正如我所提到的,您在fscanf格式字符串中的那些\n字符是不必要且具有误导性的。 To illustrate what I mean, once you get the program working, have it try to read a data file like this:为了说明我的意思,一旦你让程序工作,让它尝试读取这样的数据文件:

OFF 2 0
0 0.8267261981964111
-1.8508968353271484 0.6781123280525208
0.7865174412727356 -1.8490413427352905 0.7289819121360779

Totally malformed, but the program reads it without complaint, This is one reason (one of several dozen reasons, actually) why the scanf family of functions is basically useless for most things.完全格式错误,但程序毫无怨言地读取它,这就是为什么scanf系列函数对大多数事情基本上无用的原因之一(实际上是几十个原因之一)。 These functions claim to "scan formatted data", but their definition of "formatted" is quite loose, in that they actually read free-form input, generally without any regard for line boundaries.这些函数声称“扫描格式化数据”,但它们对“格式化”的定义相当宽松,因为它们实际上读取自由格式输入,通常不考虑行边界。

For some advice on graduating beyond scanf and using better, more reliable methods for reading input, see this question .有关从scanf毕业并使用更好、更可靠的方法来读取输入的一些建议,请参阅此问题 See also this section and this section in some online C programming course notes.另请参阅本节和一些在线 C 编程课程笔记中的本节

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM