首页 \ 问答 \ fread()中的空字符和c中的strncpy()(Null character in fread() and strncpy() in c)

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;
更新时间:2023-02-03 22:02

最满意答案

有两种正确的方法来处理标题。 我假设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 do memory[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需要内存来放置它正在读取的数据。 在您的案例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 ...
  • 这意味着,例如,如果您的源字符串是20个字符加上空终止符,并且您的strncpy指定的字符少于21个字符,那么目标字符串不会附加null。 这是因为它的工作方式: strncpy保证它将正好写入N个字节,其中N是传入的长度值。 如果源字符串的长度(sans null字节)小于该值,它将用空值填充目标区域。 如果它相等或更大,则不会将空值添加到目标。 这意味着它在技术上可能不是您获得的C字符串。 这可以通过如下代码来解决: char d[11]; // Have enough room fo ...
  • 我们不知道您使用的版本号,但我可以在这种情况下猜测。 尝试设置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 ...

相关文章

更多

最新问答

更多
  • 在csproj中使用appdata环境变量(Use appdata environment variable in csproj)
  • 从背景返回后,Skobbler Map崩溃(Skobbler Map crashes after returning from background)
  • 如何保持对绑定服务的轮询?(How to keep polling a bound service?)
  • ASP.NET单选按钮jQuery处理(ASP.NET radio button jQuery handling)
  • Linux上的FORTRAN图形库(FORTRAN graphic library on Linux)
  • 我们如何根据索引更新dynamodb表(不基于primary has和range key)(how can we update dynamodb table based on index(not based on primary has and range key))
  • 功能包装避免重复(wrap of functions avoid duplicating)
  • Android BroadcastReceiver和Activity.onPause()(Android BroadcastReceiver and Activity.onPause())
  • 无法使用phonegap 2.4在Android上播放录音(unable to play audio recordings on android using phonegap 2.4)
  • VS2015 + Resharper:不要使用C#6(VS2015 + Resharper: Don't use C#6)
  • 大学电脑四级对初学者来说要多久能过
  • 特殊字符删除?(Special characters remove?)
  • Android视频教程现在网上的都比较零散呢?有些太坑爹了,感觉老师就是在想当然的讲
  • 计算同一个表中不同行之间的差异[重复](Calculate delta's between different rows in same table [duplicate])
  • Javaweb开发,技术路线是什么?该怎么写?
  • JavaScript只在php代码中执行一次(JavaScript only executes once inside php code)
  • 不兼容的字符编码:ASCII-8BIT和UTF-8(incompatible character encodings: ASCII-8BIT and UTF-8)
  • Clojure(加载文件)给出错误(Clojure (load-file) gives an error)
  • 为具有瞬态scala依赖性的spring-xd项目优化gradle(Optimize gradle for spring-xd project with transient scala dependency)
  • 如何才能在Alpha测试模式下发布我的应用程序?(How can I publish my app in Alpha test mode only?)
  • “没有为此目标安装系统映像”Xamarin AVD Manager(“No system images installed for this target” Xamarin AVD Manager)
  • maven中的Scalatest:JUnit结果(Scalatest in maven: JUnit results)
  • 使用android SDK将文件直接上传到存储桶中的文件夹(Upload a file directly to a folder in bucket using android SDK)
  • 是否应将plists导入CoreData?(Should plists be imported to CoreData?)
  • java.lang.reflect.InvocationTargetException JavaFX TableView(java.lang.reflect.InvocationTargetException JavaFX TableView)
  • 根据唯一列值动态创建多个子集(Dynamically create multiple subsets based on unique column values)
  • 使用CSS可以使HTML锚标签不可点击/可链接吗?(Is it possible to make an HTML anchor tag not clickable/linkable using CSS?)
  • 嵌套的模板可能性(Nested template possibilities)
  • 任何方式在iOS7 +上以编程方式打开蓝牙(Any way to turn on bluetooth programmatically on iOS7+)
  • 如何为给定的SQL查询编写JPA查询(How I can write JPA query for given SQL query)