fread()中的空字符和c中的strncpy()(Null character in fread() and strncpy() in c)
我是一名新的程序员,现在我已经开始使用c了。 我正在尝试解码IDEv3 mp3标签,我遇到了各种各样的问题。 当我使用fread()和strncpy()命令时,我注意到两者都需要将\ n字符作为结束参考点。 (也许我错了,这只是观察)
当我打印输出时,它们产生一个不可读的字符。 作为解决问题的解决方案我使用fread()4字节而不是3字节来生成(8)\ n字符(整个字节),第二步我使用strncpy()和3字节分配给分配然后我用于打印的记忆。 理论上,当我使用fread()时,我不应该遇到这个问题。
代码示例:
#include <stdio.h> #include <stdlib.h> typedef struct{ unsigned char header_id[3]; /* Unsigned character 3 Bytes (24 bits) */ }mp3_Header; int main (int argc, char *argv[]) { mp3_Header first; unsigned char memory[4]; FILE *file = fopen( name.mp3 , "rb" ); if ( (size_t) fread( (void *) memory , (size_t) 4 , (size_t) 1 , (FILE *) file) !=1 ) { printf("Could not read the file\n"); exit (0); } /* End of if condition */ strncpy( (char *) first.header_id , (char *) memory , (size_t) 3); printf ("This is the header_ID: %s\n", first.header_id); fclose(file); } /* End of main */ return 0;
I am new programmer in general and I have start working now with c. I am trying to decode the IDEv3 mp3 tag and I came across with a variety of problems. While I was using the fread() and strncpy() commands I have noticed that both need to have the \n character as the end reference point. (Maybe I am wrong this is only an observation)
When I am printing the output they produce a non readable character. As a solution to overcome the problem I am using fread() for 4 Bytes instead of 3 in order to produce (8)\n characters (whole Byte), and a second step I am using strncpy() with 3 Bytes to an allocated memory which then I am using for printing. In theory when I am using fread() I should not encounter this problem.
A sample of code:
#include <stdio.h> #include <stdlib.h> typedef struct{ unsigned char header_id[3]; /* Unsigned character 3 Bytes (24 bits) */ }mp3_Header; int main (int argc, char *argv[]) { mp3_Header first; unsigned char memory[4]; FILE *file = fopen( name.mp3 , "rb" ); if ( (size_t) fread( (void *) memory , (size_t) 4 , (size_t) 1 , (FILE *) file) !=1 ) { printf("Could not read the file\n"); exit (0); } /* End of if condition */ strncpy( (char *) first.header_id , (char *) memory , (size_t) 3); printf ("This is the header_ID: %s\n", first.header_id); fclose(file); } /* End of main */ return 0;
最满意答案
有两种正确的方法来处理标题。 我假设MP3文件有IDV3标签,因此文件以“TAG”或“TAG +”开头。 因此,您要读取的部分有4个字节。
a)您认为
char *memory
是C“string”,first.header_id也是如此。 然后这样做(省略其他所有内容以显示重要部分):typedef struct{ unsigned char header_id[5]; } mp3_Header; char memory[5]; fread(memory, 4, 1, file); memory[4]='\0'; strncpy(first.header_id, memory, 5)
在恐惧之后,你的记忆看起来像这样:
0 1 2 3 4 +----+----+----+----+----+ | T | A | G | + | ? | +----+----+----+----+----+
索引4处的第5个字节未定义,因为您只读取4个字节。 如果对此字符串使用字符串函数(例如
printf("%s\n", memory)
); 该函数不知道在哪里停止,因为没有终止\ 0,并且printf将继续输出垃圾直到下一个\ 0它找到计算机RAM中的某个位置。 这就是为什么你接下来做memory[4]='\0'
所以它看起来像这样:0 1 2 3 4 +----+----+----+----+----+ | T | A | G | + | \0 | +----+----+----+----+----+
现在,您可以使用strncpy将这5个字节复制到first.header_id。 请注意,您需要复制5个字节,而不仅仅是4个字节,您还需要复制\ 0。
(在这种情况下,你也可以使用strcpy(没有n) - 它会在它遇到的第一个\ 0处停止。但是现在,为了防止缓冲区溢出,人们似乎同意不使用strcpy;相反,总是使用strncpy并明确说明接收字符串的长度)。
b)您将
memory
视为二进制数据,将二进制数据复制到标头, 然后将二进制数据转换为字符串:typedef struct{ unsigned char header_id[5]; } mp3_Header; char memory[4]; fread(memory, 4, 1, file); memcpy(first.header_id, memory, 4) first.header_id[4]='\0';
在这种情况下,内存的末尾永远不会有\ 0。 所以现在使用4字节数组就足够了。 在这种情况下(复制二进制数据),你不使用strcpy,而是使用memcpy。 这只复制了4个字节。 但是现在,
first.header_id
没有结束标记,因此您必须明确指定它。 如果不是100%清楚的话,尝试像我上面那样绘制图像。但请记住:如果您使用像'+'这样的运算符,则不会对字符串起作用。 你处理单个字符。 C语言中唯一能够处理字符串的方法是使用str *函数。
There are 2 correct ways of handling the header. I'm assuming the MP3 file has a IDV3 tag, so the file starts with "TAG" or "TAG+". So the part you want to read has 4 bytes.
a) You think of
char *memory
being a C "string", and first.header_id as well. Then do it this way (omitted everything else to show the important parts):typedef struct{ unsigned char header_id[5]; } mp3_Header; char memory[5]; fread(memory, 4, 1, file); memory[4]='\0'; strncpy(first.header_id, memory, 5)
After the fread, your memory looks like this:
0 1 2 3 4 +----+----+----+----+----+ | T | A | G | + | ? | +----+----+----+----+----+
The 5th byte, at index 4, is not defined, because you read only 4 bytes. If you use a string function on this string (for example
printf("%s\n", memory)
); the function doesn't know where to stop, because there is no terminating \0, and printf will continue to output garbage until the next \0 it finds somewhere in your computer's RAM. That's why you domemory[4]='\0'
next so it looks like this:0 1 2 3 4 +----+----+----+----+----+ | T | A | G | + | \0 | +----+----+----+----+----+
Now, you can use strncpy to copy these 5 bytes to first.header_id. Note you need to copy 5 bytes, not just 4, you want the \0 copied as well.
(In this case, you could use strcpy (without n) as well - it stops at the first \0 it encounters. But these days, to prevent buffer overflows, people seem to agree on not using strcpy at all; instead, always use strncpy and explicitly state the length of the receiving string).
b) You treat
memory
as binary data, copy the binary data to the header, and then turn the binary data into a string:typedef struct{ unsigned char header_id[5]; } mp3_Header; char memory[4]; fread(memory, 4, 1, file); memcpy(first.header_id, memory, 4) first.header_id[4]='\0';
In this case, there is never a \0 at the end of memory. So it's sufficient to use a 4-byte-array now. In this case (copying binary data), you don't use strcpy, you use memcpy instead. This copies just the 4 bytes. But now,
first.header_id
has no end marker, so you have to assign it explicitly. Try drawing images like i did above if it isn't 100% clear to you.But always remember: if you use operators like '+', you do NOT work upon the string. You work on the single characters. The only way, in C, to work on a string as a whole, is using the str* functions.
相关问答
更多-
至少有三个你可以在这里得到段错误(一个建议是启用编译器警告 - 他们经常发现“愚蠢的错误”)。 问题是test可能是错误的,它可能应该是: char *test[]={"0x11","0x12","0x13","0x00","0x00"}; 使用char*数组初始化char* ,这意味着使用该数组中的第一个指针初始化test - 这意味着test将指向字符串文字"0x11" ,因此当您使用test[3]作为参数时strncpy你将发送1转换为指针(可能是地址0x31 )。 然后strncpy会尝试写入最可 ...
-
当您的字符串嵌入了空字符时,您无法使用strlen可靠地计算字符数。 您需要捕获fread读取的字符数并使用它。 int nread = 0; while (( nread = fread (sbuff, 1, lrec, pfinput)) > 0) 代替 printf("len=%d hex=%s\n\n", strlen(sbuff), to_hex(sbuff) ); 你需要使用: printf("len=%d hex=%s\n\n", nread, to_hex(sbuff) ); ...
-
有两种正确的方法来处理标题。 我假设MP3文件有IDV3标签,因此文件以“TAG”或“TAG +”开头。 因此,您要读取的部分有4个字节。 a)您认为char *memory是C“string”,first.header_id也是如此。 然后这样做(省略其他所有内容以显示重要部分): typedef struct{ unsigned char header_id[5]; } mp3_Header; char memory[5]; fread(memory, 4, 1, file); memory[4] ...
-
我接受了我的评论,我更仔细地阅读了文档 。 您的代码传递了无效参数,并且正在调用无效参数处理程序。 也许这就是发生的事情。 即: p=(char *)malloc(sizeof(char)*strlen("Hello!\n")); 此行为7个字符分配空间,这是字符串的长度,但没有足够的空间用于空终止符 。 (这通常是一个错误) strncpy_s的文档说:这些函数试图将strSource的前D个字符复制到strDest,其中D是count的较小者和strSource的长度。 如果这些D字符符合 strDe ...
-
来自strncpy_s 文档 : 这些函数尝试将strSource的前D个字符复制到strDest,其中D是count的较小者和strSource的长度。 如果这些D字符符合strDest(其大小以numberOfElements给出)并仍为空终止符留出空间,则复制这些字符并附加终止空值; 否则,strDest [0]设置为空字符,并调用无效参数处理程序,如参数验证中所述。 让我们考虑你的代码: int len = std::strlen("no hobby"); hobby = new char[len ...
-
为什么fread segfault?(Why does fread segfault?)[2022-06-10]
fread需要内存来放置它正在读取的数据。 在您的案例sector ,第一个参数指向该内存。 但是,您还没有初始化sector 。 您将需要分配一些内存,在这种情况下只需一个字节,并指向sector 。 例如: void *sector = malloc(1); 一如既往,在使用完毕后不要忘记释放内存。 fread needs memory into which to put the data it's reading. That memory is pointed to by the first par ... -
strncpy文档问题(strncpy documentation question)[2022-08-15]
这意味着,例如,如果您的源字符串是20个字符加上空终止符,并且您的strncpy指定的字符少于21个字符,那么目标字符串不会附加null。 这是因为它的工作方式: strncpy保证它将正好写入N个字节,其中N是传入的长度值。 如果源字符串的长度(sans null字节)小于该值,它将用空值填充目标区域。 如果它相等或更大,则不会将空值添加到目标。 这意味着它在技术上可能不是您获得的C字符串。 这可以通过如下代码来解决: char d[11]; // Have enough room fo ... -
在fread中跳过并自动启动(skip and autostart in fread)[2021-09-05]
我们不知道您使用的版本号,但我可以在这种情况下猜测。 尝试设置autostart=101 。 注意详细信息的第一段?fread : 在行autostart上找到分隔符后,将确定列数。 然后从autostart向后搜索文件,直到找到没有该列数的行。 因此,找到第一个数据行,并自动跳过任何人类可读的横幅。 此功能对于加载一组可能并非全部具有一致大小横幅的文件特别有用。 设置skip>0会通过设置autostart=skip+1并关闭搜索向上步骤来覆盖此功能。 skip参数有: 如果-1(默认值)使用下面描述的过 ... -
这个: char lyrics_[song.length()]; // ^^^^^^^^^^^^^ // not a compile-time constant 是一个可变长度数组, 并不是标准C ++。 此外,您不需要将 std::string转换为字符数组。 它的种类已经是: char* lyrics = &song[0]; // assuming you don't append to song in the future 如果你真的想要一个单独的字符缓冲区 ...
-
fread以原始的二进制形式读取文件中的数据。 以数据存储的方式工作,例如fwrite 。 在处理文本时,您可以使用fscanf , 但建议不要使用它,因为它不会进行任何边界检查,这可能导致缓冲区溢出。 更好的做法是使用fgets读取文件,然后使用sscanf获取所需的值。 例如: char buffer[100]; int itemsScanned = 0; if(fgets(buffer, 100, p_finput) != NULL){ itemsScanned = sscanf(buffer ...