c_lang_Reference_040903_06

本文主要介绍C语言中文件的随机读写,包括rewind和fseek函数实现文件定位,以及fread和fwrite函数进行读写。还介绍了文件检测函数,如feof、ferror和clearerr。此外,列举了C库文件,最后对C语言文件操作进行了小结。

文字来源 VC世界--C语言教室

自我存档,也献给和我一样的DElphi程序员

文件的随机读写

  前面介绍的对文件的读写方式都是顺序读写, 即读写文件只能从头开始,顺序读写各个数据。 但在实际问题中常要求只读写文件中某一指定的部分。 为了解决这个问题可移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写。 实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。文件定位移动文件内部位置指针的函数主要有两个, 即 rewind 函数和fseek函数。

  rewind函数前面已多次使用过,其调用形式为: rewind(文件指针); 它的功能是把文件内部的位置指针移到文件首。 下面主要介绍
fseek函数。

  fseek函数用来移动文件内部位置指针,其调用形式为: fseek(文件指针,位移量,起始点); 其中:“文件指针”指向被移动的文件。 “位移量”表示移动的字节数,要求位移量是long型数据,以便在文件长度大于64KB 时不会出错。当用常量表示位移量时,要求加后缀“L”。“起始点”表示从何处开始计算位移量,规定的起始点有三种:文件首,当前位置和文件尾。
其表示方法如表10.2。
起始点    表示符号    数字表示
──────────────────────────
文件首    SEEK—SET    0
当前位置   SEEK—CUR    1
文件末尾   SEEK—END     2
例如:
fseek(fp,100L,0);其意义是把位置指针移到离文件首100个字节处。还要说明的是fseek函数一般用于二进制文件。在文本文件中由于要进行转换,故往往计算的位置会出现错误。文件的随机读写在移动位置指针之后, 即可用前面介绍的任一种读写函数进行读写。由于一般是读写一个数据据块,因此常用fread和fwrite函数。下面用例题来说明文件的随机读写。

[例10.8]在学生文件stu list中读出第二个学生的数据。
#include<stdio.h>
struct stu
{
char name[10];
int num;
int age;
char addr[15];
}boy,*qq;
main()
{
FILE *fp;
char ch;
int i=1;
qq=&boy;
if((fp=fopen("stu_list","rb"))==NULL)
{
printf("Cannot open file strike any key exit!");
getch();
exit(1);
}
rewind(fp);
fseek(fp,i*sizeof(struct stu),0);
fread(qq,sizeof(struct stu),1,fp);
printf("/n/nname/tnumber age addr/n");
printf("%s/t%5d %7d %s/n",qq->name,qq->num,qq->age,
qq->addr);
}

  文件stu_list已由例10.6的程序建立,本程序用随机读出的方法读出第二个学生的数据。程序中定义boy为stu类型变量,qq为指向boy的指针。以读二进制文件方式打开文件,程序第22行移动文件位置指针。其中的i值为1,表示从文件头开始,移动一个stu类型的长度, 然后再读出的数据即为第二个学生的数据。

文件检测函数

C语言中常用的文件检测函数有以下几个。
一、文件结束检测函数feof函数调用格式: feof(文件指针);
功能:判断文件是否处于文件结束位置,如文件结束,则返回值为1,否则为0。

二、读写文件出错检测函数ferror函数调用格式: ferror(文件指针);
功能:检查文件在用各种输入输出函数进行读写时是否出错。 如ferror返回值为0表示未出错,否则表示有错。

三、文件出错标志和文件结束标志置0函数clearerr函数调用格式: clearerr(文件指针);
功能:本函数用于清除出错标志和文件结束标志,使它们为0值。

C库文件

C系统提供了丰富的系统文件,称为库文件,C的库文件分为两类,一类是扩展名为".h"的文件,称为头文件, 在前面的包含命令中我们已多次使用过。在".h"文件中包含了常量定义、 类型定义、宏定义、函数原型以及各种编译选择设置等信息。另一类是函数库,包括了各种函数的目标代码,供用户在程序中调用。 通常在程序中调用一个库函数时,要在调用之前包含该函数原型所在的".h" 文件。
在附录中给出了全部库函数。
ALLOC.H    说明内存管理函数(分配、释放等)。
ASSERT.H    定义 assert调试宏。
BIOS.H     说明调用IBM—PC ROM BIOS子程序的各个函数。
CONIO.H    说明调用DOS控制台I/O子程序的各个函数。
CTYPE.H    包含有关字符分类及转换的名类信息(如 isalpha和toascii等)。
DIR.H     包含有关目录和路径的结构、宏定义和函数。
DOS.H     定义和说明MSDOS和8086调用的一些常量和函数。
ERRON.H    定义错误代码的助记符。
FCNTL.H    定义在与open库子程序连接时的符号常量。
FLOAT.H    包含有关浮点运算的一些参数和函数。
GRAPHICS.H   说明有关图形功能的各个函数,图形错误代码的常量定义,正对不同驱动程序的各种颜色值,及函数用到的一些特殊结构。
IO.H      包含低级I/O子程序的结构和说明。
LIMIT.H    包含各环境参数、编译时间限制、数的范围等信息。
MATH.H     说明数学运算函数,还定了 HUGE VAL 宏, 说明了matherr和matherr子程序用到的特殊结构。
MEM.H     说明一些内存操作函数(其中大多数也在STRING.H 中说明)。
PROCESS.H   说明进程管理的各个函数,spawn…和EXEC …函数的结构说明。
SETJMP.H    定义longjmp和setjmp函数用到的jmp buf类型, 说明这两个函数。
SHARE.H    定义文件共享函数的参数。
SIGNAL.H    定义SIG[ZZ(Z] [ZZ)]IGN和SIG[ZZ(Z] [ZZ)]DFL常量,说明rajse和signal两个函数。
STDARG.H    定义读函数参数表的宏。(如vprintf,vscarf函数)。
STDDEF.H    定义一些公共数据类型和宏。
STDIO.H    定义Kernighan和Ritchie在Unix System V 中定义的标准和扩展的类型和宏。还定义标准I/O 预定义流:stdin,stdout和stderr,说明 I/O流子程序。
STDLIB.H    说明一些常用的子程序:转换子程序、搜索/ 排序子程序等。
STRING.H    说明一些串操作和内存操作函数。
SYS/STAT.H   定义在打开和创建文件时用到的一些符号常量。
SYS/TYPES.H  说明ftime函数和timeb结构。
SYS/TIME.H   定义时间的类型time[ZZ(Z] [ZZ)]t。
TIME.H     定义时间转换子程序asctime、localtime和gmtime的结构,ctime、 difftime、 gmtime、 localtime和stime用到的类型,并提供这些函数的原型。
VALUE.H    定义一些重要常量, 包括依赖于机器硬件的和为与Unix System V相兼容而说明的一些常量,包括浮点和双精度值的范围。

小结
1. C系统把文件当作一个“流”,按字节进行处理。

2. C文件按编码方式分为二进制文件和ASCII文件。

3. C语言中,用文件指针标识文件,当一个文件被 打开时, 可取得该文件指针。

4. 文件在读写之前必须打开,读写结束必须关闭。

5. 文件可按只读、只写、读写、追加四种操作方式打开,同时还必须指定文件的类型是二进制文件还是文本文件。

6. 文件可按字节,字符串,数据块为单位读写,文件也可按指定的格式进行读写。

7. 文件内部的位置指针可指示当前的读写位置,移动该指针可以对文件实现随机读写

<think> 在Java中,并没有直接等同于C/C++的`__FUNCTION__`宏的内置功能。不过,Java提供了一些机制来获取类似的信息,主要是通过`StackTraceElement`类或者使用反射API。以下是几种常见的方法: 1. **使用`Thread.currentThread().getStackTrace()`** 可以通过当前线程的堆栈跟踪来获取当前执行的方法名。 示例代码: ```java public class FunctionNameExample { public static void main(String[] args) { String methodName = new Object(){}.getClass().getEnclosingMethod().getName(); System.out.println("Current method: " + methodName); // 输出:main new FunctionNameExample().exampleMethod(); } public void exampleMethod() { String methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("Current method: " + methodName); // 输出:exampleMethod } } ``` 注意:`Thread.currentThread().getStackTrace()`返回的是一个`StackTraceElement`数组,其中索引0表示栈顶(当前方法),索引1表示调用当前方法的方法(即上一层)。但要注意,不同的JVM实现可能有所不同,所以通常我们通过遍历堆栈来查找合适的位置。 另一种更简洁的方式(在Java 9+中推荐使用`StackWalker`): ```java import java.lang.StackWalker; public class StackWalkerExample { public static void main(String[] args) { new StackWalkerExample().exampleMethod(); } public void exampleMethod() { StackWalker walker = StackWalker.getInstance(); String methodName = walker.walk(frames -> frames.findFirst().map(StackWalker.StackFrame::getMethodName).orElse("Unknown")); System.out.println("Current method: " + methodName); // 输出:exampleMethod } } ``` 2. **使用`Throwable`** 也可以创建一个`Throwable`对象(但不抛出)来获取堆栈信息。 示例代码: ```java public class ThrowableExample { public void exampleMethod() { String methodName = new Throwable().getStackTrace()[0].getMethodName(); System.out.println("Current method: " + methodName); } } ``` 3. **使用匿名内部类(在静态方法中)** 对于静态方法,可以使用匿名内部类来获取方法名(但这种方式只能用于非静态上下文中获取当前方法名): ```java public class AnonymousClassExample { public static void main(String[] args) { new AnonymousClassExample().exampleMethod(); } public void exampleMethod() { String methodName = new Object(){}.getClass().getEnclosingMethod().getName(); System.out.println("Current method: " + methodName); } } ``` 4. **使用AOP(面向切面编程)** 在更复杂的场景中,可以使用AOP框架(如AspectJ)在方法执行时自动捕获方法名。这种方法通常用于日志记录等横切关注点。 **总结**: - 在Java中,获取当前方法名最常用的方式是通过`Thread.currentThread().getStackTrace()`或`Throwable`的堆栈跟踪。 - 在Java 9及以上版本,推荐使用`StackWalker`,因为它更高效且更安全。 - 注意,这些方法都有一定的性能开销,因此在性能敏感的代码中应谨慎使用。 如果你需要在日志中输出当前方法名,可以考虑使用日志框架(如SLF4J、Log4j2)的占位符功能,它们通常提供了方便的方法来输出方法名。例如,在Log4j2中可以使用`%method`。 §§相关问题§§ 1. Java 9的`StackWalker`相比传统的堆栈跟踪方法有什么优势? 2. 如何在Java中获取调用者的方法名? 3. Java中是否有类似C/C++的`__LINE__`和`__FILE__`的功能? </think> 在Java中,并没有直接等同于C/C++的`__FUNCTION__`宏的内置功能,但可以通过以下方式实现类似效果: ### 1. 获取方法名(推荐方式) 使用`StackTraceElement`获取当前执行的方法名: ```java public class FunctionName { public static void main(String[] args) { new FunctionName().printMethodName(); } public void printMethodName() { String methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); System.out.println("当前方法: " + methodName); // 输出: printMethodName } } ``` ### 2. 使用反射获取方法名 通过`getEnclosingMethod()`获取封装方法名: ```java import java.lang.reflect.Method; public class ReflectionExample { public static void main(String[] args) throws Exception { Method method = ReflectionExample.class.getMethod("exampleMethod"); System.out.println("方法名: " + method.getName()); // 输出: exampleMethod } public static void exampleMethod() { // 通过匿名内部类获取 String name = new Object(){}.getClass().getEnclosingMethod().getName(); System.out.println("当前方法: " + name); // 输出: exampleMethod } } ``` ### 3. Java 9+ 的`StackWalker`(最佳实践) Java 9引入了更高效的`StackWalker` API: ```java import java.lang.StackWalker; public class StackWalkerExample { public static void main(String[] args) { new StackWalkerExample().logMethodName(); } public void logMethodName() { StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); String methodName = walker.walk(frames -> frames.findFirst().map(StackWalker.StackFrame::getMethodName).orElse("未知方法") ); System.out.println("执行方法: " + methodName); // 输出: logMethodName } } ``` ### 关键区别说明 | 特性 | C/C++ `__FUNCTION__` | Java 替代方案 | |---------------------|---------------------------|------------------------------| | **获取方式** | 预处理器宏 | 运行时反射/堆栈跟踪 | | **性能影响** | 零开销 | 有运行时开销 | | **返回内容** | 当前函数名 | 当前方法名 | | **编译时确定** | ✓ | ✗ (运行时确定) | | **线程安全** | ✓ | ✓ | ### 实际应用场景 1. **日志记录**:自动添加调用方法名 ```java void processData() { logger.debug("进入方法: {}", getCurrentMethodName()); // ...业务逻辑... } ``` 2. **调试信息**:异常处理中自动添加上下文 ```java try { riskyOperation(); } catch (Exception e) { throw new RuntimeException("在" + getMethodName() + "中出错", e); } ``` 3. **AOP编程**:结合Spring AOP实现自动方法追踪 ```java @Aspect public class LoggingAspect { @Before("execution(* com.example..*.*(..))") public void logMethod(JoinPoint joinPoint) { System.out.println("调用方法: " + joinPoint.getSignature().getName()); } } ``` > **性能提示**:在生产环境中频繁获取方法名会影响性能,建议仅在调试/日志场景使用。对于高性能场景,可使用`-XX:-OmitStackTraceInFastThrow` JVM参数优化[^1]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值