gpt4 book ai didi

c - C-文件的read()错误,数组存储和正确打印输出

转载 作者:行者123 更新时间:2023-12-04 10:45:36 25 4
gpt4 key购买 nike

我是C语言的新手,所以我不确定自己的错误在哪里。但是,我确实知道问题的很大一部分在于我如何将double存储在d_buffer(double)数组中或打印它的方式。

具体来说,我的输出始终打印出非常大的数字(小数点前大约有10-12位数字,小数点后有零尾。此外,这是对较旧程序的改编,允许双输入,因此我只添加了两个if语句(在“ read” for循环和“ printf” for循环中)和d_buffer声明。

我将不胜感激,因为我花了几个小时解决此错误。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


struct DataDescription
{
char fieldname[30];
char fieldtype;
int fieldsize;
};




/* -----------------------------------------------
eof(fd): returns 1 if file `fd' is out of data
----------------------------------------------- */
int eof(int fd)
{
char c;

if ( read(fd, &c, 1) != 1 )
return(1);
else
{ lseek(fd, -1, SEEK_CUR);
return(0);
}
}


void main()
{
FILE *fp; /* Used to access meta data */
int fd; /* Used to access user data */

/* ----------------------------------------------------------------
Variables to hold the description of the data - max 10 fields
---------------------------------------------------------------- */
struct DataDescription DataDes[10]; /* Holds data descriptions
for upto 10 fields */
int n_fields; /* Actual # fields */

/* ------------------------------------------------------
Variables to hold the data - max 10 fields....
------------------------------------------------------ */
char c_buffer[10][100]; /* For character data */
int i_buffer[10]; /* For integer data */
double d_buffer[10];

int i, j;
int found;

printf("Program for searching a mini database:\n");

/* =============================
Read in meta information
============================= */
fp = fopen("db-description", "r");
n_fields = 0;
while ( fscanf(fp, "%s %c %d", DataDes[n_fields].fieldname,
&DataDes[n_fields].fieldtype,
&DataDes[n_fields].fieldsize) > 0 )
n_fields++;

/* ---
Prints meta information
--- */
printf("\nThe database consists of these fields:\n");
for (i = 0; i < n_fields; i++)
printf("Index %d: Fieldname `%s',\ttype = %c,\tsize = %d\n",
i, DataDes[i].fieldname, DataDes[i].fieldtype,
DataDes[i].fieldsize);
printf("\n\n");

/* ---
Open database file
--- */
fd = open("db-data", O_RDONLY);

/* ---
Print content of the database file
--- */
printf("\nThe database content is:\n");
while ( ! eof(fd) )
{ /* ------------------
Read next record
------------------ */
for (j = 0; j < n_fields; j++)
{
if ( DataDes[j].fieldtype == 'I' )
read(fd, &i_buffer[j], DataDes[j].fieldsize);
if ( DataDes[j].fieldtype == 'F' )
read(fd, &d_buffer[j], DataDes[j].fieldsize);
if ( DataDes[j].fieldtype == 'C' )
read(fd, &c_buffer[j], DataDes[j].fieldsize);
}

double d;
/* ------------------
Print it...
------------------ */
for (j = 0; j < n_fields; j++)
{
if ( DataDes[j].fieldtype == 'I' )
printf("%d ", i_buffer[j]);
if ( DataDes[j].fieldtype == 'F' )
d = d_buffer[j];
printf("%lf ", d);
if ( DataDes[j].fieldtype == 'C' )
printf("%s ", c_buffer[j]);
}
printf("\n");
}
printf("\n");
printf("\n");


}


预期产量:
3行数据以数字“ e = 2.18281828”结尾

要重现此问题,以下两个文件必须与lookup-data.c文件位于同一目录中:
-[db-data] [1]
-[db-description] [2]

最佳答案

编辑:我以前的猜测都是错误的。问题在于数据库文件具有大端数字,并且数据是在小端计算机上读取的。跳到标有“在这里开始阅读”的部分,以避免浪费您的时间进行早期猜测(此处保留其非常有限的历史价值)。

我怀疑您的问题与用于打印值的printf()格式规范%lf以及声明为d_buffer类型的事实有关。 int可能为true,因此您将数据的额外字节解释为浮点值的一部分。

我不确定这一点,因为我看不到程序用来运行的数据,但是如果您的浮点数据sizeof(double) > sizeof(int)fieldsize而不是sizeof(float),则可能是将浮点值存储到sizeof(double)很好,但是在打印时将它们弄乱了。或者,如果d_buffer等于fieldsize,并且sizeof(double)大于sizeof(double),则您要注销sizeof(int)的末尾,然后某些数据将损坏。

我建议您将声明更改为d_buffer,看看您的程序是否运行更好。另外,请查看double d_buffer[10];是否设置为fieldsizesizeof(float);如果是sizeof(double),则将sizeof(float)声明为d_buffer类型,并使用以下代码:

  if ( DataDes[j].fieldtype == 'F' )
{
double d = d_buffer[j];
printf("%lf ", d);
}


编辑:另外,我建议您切换到对所有I / O使用 floatfopen()。基本的 fread()open()可以返回 read(),这意味着您需要重试该操作,因此仅在循环中正确使用它们,如果循环返回 EINTR,则该循环将重试该调用。 EINTRfopen()调用将您与此类细节隔离开来,并具有一些缓冲,可以使您的程序更流畅地运行。 (我很确定您当前的项目只是读写少量数据,因此,此时的性能差异对您而言并不重要。)

另外,我建议您摆脱 fread()函数;这是非常不寻常的,读取字符然后使用 eof()放回字符可能会有点慢。 C库具有函数 fseek(),该函数从输入流中获取一个字符,您可以测试该字符以查看其是否为检测文件结尾的 fgetc()值。 C库还具有 EOF函数,可以后退一个字符。这实际上并不涉及寻找磁盘,而只是将字符放回某个地方的缓冲区中。但是您甚至不需要为代码使用 ungetc()fgetc();只需检查 ungetc()的返回值,如果读取的长度为零,就知道您命中了文件结尾。无论如何,在生产代码中,您将需要检查每个函数调用的返回值。您不可能希望每次读取都能成功。

编辑:您可以尝试做的另一件事:将格式代码从 fread()更改为 "%lf",然后看看会发生什么。我不确定 "%f"会做什么,您不需要它。普通的旧 l应该格式化 "%f"。但这可能不会改变任何东西:根据我发现的此网页,在 doubleprintf()之间没有区别。

http://www.dgp.toronto.edu/~ajr/209/notes/printf.html

*在这里开始阅读*

编辑:好的,我已经确定了一件事。数据库格式是索引值(整数值),然后是浮点值,然后是字符串值。您将需要阅读每一个,以前进文件中的当前位置。因此,您当前的代码正在检查格式代码并确定要读取的内容?不正确;您需要为每个记录读取一个整数,一个浮点数和一个字符串。

编辑:好的,这是一个可以读取数据库的正确的Python程序。读取元数据文件并不麻烦。我只是对常数进行了硬编码(没关系,因为它只是一个一次性程序)。

import struct

_format_rec = ">id20s" # big-endian: int, double, 20-char string
_cb_rec = struct.calcsize(_format_rec) # count of bytes in this format

def read_records(fname):
with open(fname) as in_f:
try:
while True:
idx, f, s = struct.unpack(_format_rec, in_f.read(_cb_rec))
# Python doesn't chop at NUL byte by default so do it now.
s, _, _ = s.partition('\0')
yield (idx, f, s)
except struct.error:
pass

if __name__ == "__main__":
for i, (idx, f, s) in enumerate(read_records("db-data")):
print "%d) index: %d\tfloat: %f \ttext: \"%s\"" % (i, idx, f, s)


因此索引值为32位整数big-endian。浮点数是big-endian 64位浮点数;并且文本字段固定为20个字符(因此,字符串是0到19个字符,再加上一个终止NUL字节)。

这是上面程序的输出:

0) index: 1     float: 3.141593         text: "Pi"
1) index: 2 float: 12.345000 text: "Secret Key"
2) index: 3 float: 2.718282 text: "The number E"


现在,当我尝试编译您的C代码时,由于我的计算机是低端字节顺序的,因此我得到了垃圾值。您是否要在小端计算机上运行C代码?

编辑:要回答最近的评论,这是一个问题:对于每个输入记录,您必须调用 "%lf" 3次。第一次读取索引,它是一个4字节的整数(big-endian)。第二次读取是一个8字节浮点值,也是big-endian。第三次读取为20个字节的字符串。每次读取都会移动文件中的当前位置。这三个读取一起从文件中读取一条记录。阅读并打印了三个记录之后,您就完成了。

由于我的计算机是低位字节序的,因此在C中正确获取值很困难,但是我做到了。我创建了一个联合,使我可以读取8字节值作为整数或浮点数,并将其用作对 "%f"的调用的缓冲区。然后我调用 read()(GCC中的一个功能)将big-endian值交换为little-endian,将结果存储为64位整数,然后将其读取为浮点型。我还使用 read()交换整数索引。我的C程序现在可以打印:

The database content is:
1 3.141593 Pi
2 12.345000 Secret Key
3 2.718282 The number E


因此,请阅读每条记录,确保您对数据的字节序正确,并且您将拥有一个正常运行的程序。

编辑:这是一些代码片段,它们显示了我如何解决字节序问题。

typedef union
{
unsigned char buf[8];
double d;
int64_t i64;
int32_t i32;
} U;

// then, inside of main():

printf("\nThe database content is:\n");
{ /* ------------------
Read next record
------------------ */
for (j = 0; j < n_fields; j++)
{
U u;
read(fd, u.buf, 4);
u.i32 = __builtin_bswap32(u.i32);
i_buffer[j] = u.i32;
read(fd, u.buf, 8);
u.i64 = __builtin_bswap64(u.i64);
d_buffer[j] = u.d;
read(fd, c_buffer[j], 20);
}


我对数据库采用大端格式感到惊讶。任何使用x86系列处理器的计算机都是低端字节序。

您绝对不应该像我一样对这些数字(4、8、20)进行硬编码。您应该使用收到的元数据。我留给你

编辑:另外,您不应该调用 __builtin_bswap64()__builtin_bswap32()。您应该调用 __builtin_bswap32(),然后...我不确定64位是什么。但是 __builtin_bswap64()是可移植的;如果您在大端计算机上进行编译,它将跳过交换;如果在小端计算机上进行编译,则将进行交换。

编辑:我在StackOverflow上找到了一个等效于 ntohl()的64位解决方案。

https://stackoverflow.com/a/4410728/166949

如果只关心Linux,这很简单:您可以先使用 ntohl(),然后使用 ntohl()#include <arpa/inet.h>,而不是使用 #include <endian.h>

一旦有了这个,就可以像这样读取数据库文件:

u.i64 = be64toh(u.i64);


如果在big-endian机器上编译上述代码,它将编译为空操作并读取big-endian值。如果在Little-endian机器上编译上述代码,它将编译为 be32toh()的等效项并交换字节,以便正确读取64位值。

编辑:我在几个地方说过,您需要分别进行三个读取:一个读取索引,一个读取浮点数和一个读取字符串。实际上,您可以声明 be64toh()并发出单个读取,该读取将一次读取所有数据。但是,棘手的部分是C编译器可能在 __builtin_bswap64()中插入“填充”字节,这将导致 struct的字节结构与从文件中读取的记录的字节结构不完全匹配。 C编译器应提供一种控制对齐字节的方法( struct语句),但我不想深入了解它。对于这个简单的程序,只需阅读三遍即可解决问题。

关于c - C-文件的read()错误,数组存储和正确打印输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11321327/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com