前言:
第1节是笔者基于公司的“祖传”C语言操作模板上做相应修改的程序,当做自己以后使用C语言文件操作的模板。
第2节与第3节是笔者之前需要配合团队内的小伙伴做数据转换,他需要我将保存在.txt文件内的字符数据(十六进制)转换为二进制,也需要我将.Bin文件内的二进制数据转换为字符数据(十六进制)。当然这两节程序有很大的优化空间,弊端为每次读取数据的量太小,需要频繁IO操作,导致运行速度变得很慢。解决方法为使用malloc函数一次性将文件的内容读取到内存空间上,可大幅提高程序的运行速度。
第4节是笔者一直很好奇32位操作系统下的C语言的数据类型所占空间进行测试的例子,方便自己在后面的开发中准确的使用数据类型,不至于让数据小而空间大,或者数据大而空间小。
第5节是笔者想通过C语言为工程项目中修改配置文件中的参数,通过匹配配置文件中指定的变量去替换参数。但该节程序最后并未用于工程项目中,笔者想表达,C语言实现真的好麻烦,这就是更高级的编程语言强大之处。
第6节是笔者想为嵌入式Linux产品的开机Logo动态读取指定路径下的.ppm图片,原本考虑修改Kernel中显示Logo部分,将图片的数据写入相应的地方。但后面在内核源码中找不到内核编译时怎么将logo_linux_clut224.ppm编译为logo_linux_clut224.c的方法,不清楚.ppm文件是怎么将数据转化到struct linux_logo结构体中的clut和data成员,后面不得不放弃这种想法。但也让笔者改变思路,让驱动程序读取指定路径下的配置文件,让驱动定向运行。
第7节为应用层的常规文件操作。
第8节为获取挂载在文件系统下节点的存储情况。
第9节摘抄自《Linux程序设计 第4版》中第3、4节的C语言进行文件/目录、系统操作
第10节为记录程序运行的时间
下面的程序参考了网上的许多例子,测试可用,若需要使用,需要修改部分参数。
1. C语言文件操作的模板:
// 编译与运行命令:
// gcc xxxx.c -o xxxx
// ./xxxx
#include <stdio.h>
#include <string.h>
int OpenFile(char *ReadFile, char *WriteFile, unsigned int *FileLength,
FILE **fpRead, FILE **fpWrite);
void CloseFile(FILE *fpRead, FILE *fpWrite);
int FileOperation(unsigned int FileLength, FILE *fpRead, FILE *fpWrite);
int main(int argc, char *argv[])
{
char ReadFile[] = "ReadFile.txt";
char WriteFile[] = "WriteFile.txt";
FILE *fpRead = NULL, *fpWrite = NULL;
unsigned int FileLength = 0;
int ret = 0;
// 打开需要读、写的文件
ret = OpenFile(ReadFile, WriteFile, &FileLength, &fpRead, &fpWrite);
if(ret < 0)
{
return 0;
}
// 文件操作
FileOperation(FileLength, fpRead, fpWrite);
// 关闭文件
CloseFile(fpRead, fpWrite);
return 0;
}
int OpenFile(char *ReadFile, char *WriteFile, unsigned int *FileLength,
FILE **fpRead, FILE **fpWrite)
{
// 打开需要读的文件
*fpRead = fopen(ReadFile, "r+");
if(*fpRead == NULL)
{
printf("open %s error \n", ReadFile);
return -1;
}
// 打开需要写的文件
*fpWrite = fopen(WriteFile, "wb");
if(*fpWrite == NULL)
{
printf("open %s error \n", WriteFile);
fclose(*fpRead);
return -1;
}
// 将读文件指针定位于文件的末尾处,获取文件大小
fseek(*fpRead, 0, SEEK_END);
*FileLength = ftell(*fpRead);
printf("FileLength = %dB (%.2fMB) \n", *FileLength,
((double)*FileLength / (1024 * 1024)));
return 1;
}
void CloseFile(FILE *fpRead, FILE *fpWrite)
{
fclose(fpRead);
fclose(fpWrite);
}
int FileOperation(unsigned int FileLength, FILE *fpRead, FILE *fpWrite)
{
unsigned int i = 0;
unsigned char Array[3];
unsigned char CharLen = 2;
// 将读、写文件指针指向文件头
fseek(fpRead, 0, SEEK_SET);
fseek(fpWrite, 0, SEEK_SET);
// 进行文件读写
for(i = 0; i < FileLength; i += 2)
{
if((i != 0) && (i % 16 == 0))
{
fwrite("\n", sizeof(unsigned char), 1, fpWrite);
}
memset(Array, '\0', sizeof(Array));
fread(Array, sizeof(unsigned char), CharLen, fpRead);
fwrite(Array, sizeof(unsigned char), CharLen, fpWrite);
fwrite(" ", sizeof(unsigned char), 1, fpWrite);
}
return 1;
}
FILE结构体:
// 位于stdio.h,笔者在Linux底下找不到定义FILE结构体,只在Visual Studio内找到了定义
// 不同编译器下的FILE结构体定义不一样
// FILE结构是间接地操作系统的文件控制块 (FCB)来实现对文件的操作
#ifndef _FILE_DEFINED
struct _iobuf {
char *_ptr; // 文件输入的下一个位置
int _cnt; // 当前缓冲区的相对位置
char *_base; // 指基础位置(即是文件的其始位置)
int _flag; // 文件标志
int _file; // 应该是文件描述符,进入打开文件列表索引的整数
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif
2. 将字符(十六进制)转成二进制(C语言的文件操作):
// 编译与运行命令:
// gcc xxxx.c -o xxxx -lm
// ./xxxx
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <unistd.h>
#include <sys/time.h>
int main(int argc, char *argv[])
{
char FilePath[] = "/mnt/hgfs/XXXX/";
char ReadFile[] = "File";
char WriteFile[] = "FileConvert";
char TxtUnit[] = ".txt";
char BinUnit[] = ".bin";
char ReadFilePath[80], WriteFilePath[80];
char ClearWriteFileCmd[160], TxtToJPEGCmd[160];
unsigned int FileLength = 0;
char array[4];
unsigned char DataBin = 0;
unsigned char DataTemp = 0;
int i = 0, j = 0;
FILE *fpRead = NULL, *fpWrite = NULL;
unsigned int NumRead = 0, NumWrite = 0;
unsigned int DataCount = 0;
int CharLen = 2; // 每次读取文件的两个字符
memset(ReadFilePath, 0, 80 * sizeof(char));
memset(WriteFilePath, 0, 80 * sizeof(char));
memset(ClearWriteFileCmd, 0, 160 * sizeof(char));
memset(TxtToJPEGCmd, 0, 160 * sizeof(char));
sprintf(ReadFilePath, "%s%s%s", FilePath, ReadFile, TxtUnit);
sprintf(WriteFilePath, "%s%s%s", FilePath, WriteFile, TxtUnit);
sprintf(ClearWriteFileCmd, "dd if=/dev/null of=%s%s%s", FilePath, WriteFile, TxtUnit);
sprintf(TxtToJPEGCmd, "mv %s%s%s %s%s%s", FilePath, WriteFile, TxtUnit, FilePath, WriteFile, BinUnit);
// 打开需要读的文件
fpRead = fopen(ReadFilePath , "r+");
if(fpRead == NULL)
{
printf("Open %s error \n", ReadFilePath);
return -1 ;
}
// 打开需要写的文件
fpWrite = fopen(WriteFilePath , "wb");
if(fpWrite == NULL)
{
printf("Open %s error \n", WriteFilePath);
return -1 ;
}
printf("Open %s Success \n", ReadFilePath);
printf("Open %s Success \n", WriteFilePath);
system(ClearWriteFileCmd);
// 定位于文件的末尾处,获取文件大小
fseek(fpRead, 0, SEEK_END);
FileLength = ftell(fpRead);
printf("FileLength = %d (%.2fK %.2fM) \n", FileLength, ((double)FileLength / 1024), ((double)FileLength / 1024 / 1024));
// 将文件指针指向文件头
fseek(fpRead, 0, SEEK_SET);
fseek(fpWrite, 0, SEEK_SET);
NumRead = 0;
NumWrite = 0;
while(NumRead < FileLength)
{
memset(&array, '\0', 4 * sizeof(char));
fseek(fpRead, NumRead, SEEK_SET);
fread(array, sizeof(char), CharLen, fpRead);
// 判断取出的值是否为换行符、空格、制表符、回车符
//if((array[0] == '\n') || (array[0] == ' ') || (array[0] == '\t') || (array[0] == '\r'))
if(array[0] <= ' ')
{
NumRead += (1 * sizeof(char));
continue ;
}
NumRead += (CharLen * sizeof(char));
// 下面这句话不能写,会大大拖慢速度,运行速度降低150倍左右
//fseek(fpWrite, NumWrite, SEEK_SET);
// 字符转为数据
for(DataBin = 0 , j = 0 ; j < CharLen ; j ++)
{
DataTemp = array[j] ;
if((DataTemp >= '0') && (DataTemp <= '9')) DataTemp = DataTemp - '0';
else if((DataTemp >= 'a') && (DataTemp <= 'z')) DataTemp = (DataTemp - 'a' + 10);
else if((DataTemp >= 'A') && (DataTemp <= 'Z')) DataTemp = (DataTemp - 'A' + 10);
//DataBin += (DataTemp * pow(16, (CharLen - 1 - j)));
DataBin += (DataTemp << (4 * (CharLen - 1 - j)));
}
fwrite(&DataBin, sizeof(char), 1, fpWrite);
NumWrite += (1 * sizeof(char));
DataCount ++ ;
}
fclose(fpRead);
fclose(fpWrite);
system(TxtToJPEGCmd);
return 0 ;
}
3. 将二进制转成字符(十六进制)(C语言的文件操作):
// 编译与运行命令:
// gcc xxxx.c -o xxxx -lm
// ./xxxx
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <unistd.h>
#include <sys/time.h>
int FileConvert(char *FilePath, char *ReadFile, char *WriteFile);
int main(int argc, char *argv[])
{
char FilePath[] = "/mnt/hgfs/Share_Ubuntu/FileDispose/Bin01/";
char FileName[] = "rgb";
char ReadFile[50], WriteFile[50];
unsigned int FileCounter = 1, FileNum = 100;
int ret = 0;
while(FileCounter <= FileNum)
{
memset(ReadFile, 0, sizeof(ReadFile));
memset(WriteFile, 0, sizeof(WriteFile));
sprintf(ReadFile, "%s%02d", FileName, FileCounter);
sprintf(WriteFile, "%sConvert", ReadFile);
FileCounter++;
ret = FileConvert(FilePath, ReadFile, WriteFile);
if(ret < 0)
{
printf("Read and Write %s error \n", ReadFile);
continue;
}
}
return 0 ;
}
int FileConvert(char *FilePath, char *ReadFile, char *WriteFile)
{
FILE *fpRead = NULL, *fpWrite = NULL;
int CharLen = 1; // 每次读取文件的一个字符
unsigned char array[4];
unsigned char DataBin = 0;
unsigned char DataTemp = 0;
int i = 0, j = 0;
unsigned int DataCount = 0;
char TxtUnit[] = ".txt", JpgUnit[] = ".jpg", BinUnit[] = ".bin";
char ReadFilePath[80], WriteFilePath[80];
char ClearWriteFileCmd[160];
memset(ReadFilePath, 0, sizeof(ReadFilePath));
memset(WriteFilePath, 0, sizeof(WriteFilePath));
memset(ClearWriteFileCmd, 0, 160 * sizeof(char));
sprintf(ReadFilePath, "%s%s%s", FilePath, ReadFile, BinUnit);
sprintf(WriteFilePath, "%s%s%s", FilePath, WriteFile, TxtUnit);
sprintf(ClearWriteFileCmd, "dd if=/dev/null of=%s > /dev/null 2>&1", WriteFilePath);
// 打开需要读的文件
fpRead = fopen(ReadFilePath , "r+");
if(fpRead == NULL)
{
printf("Open %s error \n", ReadFilePath);
return -1 ;
}
// 打开需要写的文件
fpWrite = fopen(WriteFilePath , "wb");
if(fpWrite == NULL)
{
printf("Open %s error \n", WriteFilePath);
return -1 ;
}
system(ClearWriteFileCmd);
printf("Open %s Success \n", ReadFilePath);
printf("Open %s Success \n", WriteFilePath);
unsigned int FileLength = 0;
// 定位于文件的末尾处,获取文件大小
fseek(fpRead, 0, SEEK_END);
FileLength = ftell(fpRead);
printf("(%s) FileLength = %d B (%.2f KB, %.2f MB) \n", ReadFile,
FileLength,
((double)(FileLength >> 10)),
((double)(FileLength >> 20)));
// 将文件指针指向文件头
fseek(fpRead, 0, SEEK_SET);
fseek(fpWrite, 0, SEEK_SET);
unsigned int NumRead = 0, NumWrite = 0;
while(NumRead < FileLength)
{
if((NumRead != 0) && (NumRead % 3 == 0))
{
fwrite(" ", sizeof(char), 1, fpWrite);
}
memset(&DataBin, '\0', sizeof(DataBin));
memset(array, '\0', sizeof(array));
fread(&DataBin, sizeof(unsigned char), CharLen, fpRead);
NumRead += (CharLen * sizeof(unsigned char));
array[0] = (DataBin & 0xF0) >> 4; // 提取十位
array[1] = (DataBin & 0x0F) >> 0; // 提取个位
// printf("DataBin = 0x%X, array[0] = 0x%X, array[1] = 0x%X \n", DataBin, array[0], array[1]);
// 二进制转换为字符的计算
for(j = 0 ; j < 2 ; j ++)
{
if((array[j] >= 0x0) && (array[j] <= 0x9)) array[j] = array[j] + '0';
else if((array[j] >= 0xA) && (array[j] <= 0xF)) array[j] = array[j] - 0xA + 'A';
}
fwrite(&array, sizeof(char), 2, fpWrite);
}
fclose(fpRead);
fclose(fpWrite);
}
4. 验证char系列、int系列、long系列、float系列、struct类型的存储空间,数据大小端的存储空间
当前目录的文件结构:
tree test/
test/
├── 1.h
├── a.c
├── b.c
├── c.c
└── Makefile
4.1 1.h文件内容:
#ifndef __1_H
#define __1_H
#include <stdio.h>
#include <float.h>
#include <limits.h>
struct sData{
unsigned int uint32_data_1;
unsigned long long uint64_data_1;
unsigned char uint8_data_1;
unsigned char uint8_data_2;
unsigned int uint32_data_2;
unsigned short int uint16_data_1;
unsigned char uint8_data_3;
unsigned int uint32_data_3;
};
int b_fun(void);
int c_fun(void);
#endif
4.2 a.c文件内容:
#include <stdio.h>
#include "1.h"
int main(int argc , char* argv[])
{
b_fun();
c_fun();
return 0;
}
4.3 b.c文件内容:
#include "1.h"
#include "string.h"
int b_fun(void)
{
printf("\n== b_fun == \n");
printf("\n============ char ============\n");
printf("sizeof(char) = %d \n", sizeof(char)); // 1
printf("sizeof(unsigned char) = %d \n", sizeof(unsigned char)); // 1
printf("sizeof(signed char) = %d \n", sizeof(signed char)); // 1
printf("\n============ short int ============\n");
printf("sizeof(short int) = %d \n", sizeof(short int)); // 2
printf("sizeof(unsigned short int) = %d \n", sizeof(unsigned short int)); // 2
printf("sizeof(signed short int) = %d \n", sizeof(signed short int)); // 2
printf("\n============ int ============\n");
printf("sizeof(int) = %d \n", sizeof(int)); // 4
printf("sizeof(unsigned int) = %d \n", sizeof(unsigned int)); // 4
printf("sizeof(signed int) = %d \n", sizeof(signed int)); // 4
printf("\n============ long int ============\n");
printf("sizeof(long int ) = %d \n", sizeof(long int)); // 4
printf("sizeof(unsigned long int) = %d \n", sizeof(unsigned long int)); // 4
printf("sizeof(signed long int) = %d \n", sizeof(signed long int)); // 4
printf("\n============ long long ============\n");
printf("sizeof(long long) = %d \n", sizeof(long long)); // 8
printf("sizeof(signed long long) = %d \n", sizeof(signed long long)); // 8
printf("sizeof(unsigned long long) = %d \n", sizeof(unsigned long long)); // 8
printf("\n============ float ============\n");
printf("sizeof(float) = %d \n", sizeof(float)); // 4
printf("\n============ double ============\n");
printf("sizeof(double) = %d \n", sizeof(double)); // 8
printf("\n\n");
unsigned char uint8_Array[10];
unsigned short int uint16_Array[10];
unsigned int uint32_Array[10];
unsigned long long uint64_Array[10];
float float32_Array[10];
double float64_Array[10];
memset(uint8_Array, '\0', 10 * sizeof(unsigned char));
memset(uint16_Array, '\0', 10 * sizeof(unsigned short int));
memset(uint32_Array, '\0', 10 * sizeof(unsigned int));
memset(uint64_Array, '\0', 10 * sizeof(unsigned long long));
memset(float32_Array, '\0', 10 * sizeof(float));
memset(float64_Array, '\0', 10 * sizeof(double));
printf("sizeof(uint8_Array) = %d \n", sizeof(uint8_Array));
printf("sizeof(uint16_Array) = %d \n", sizeof(uint16_Array));
printf("sizeof(uint32_Array) = %d \n", sizeof(uint32_Array));
printf("sizeof(uint64_Array) = %d \n", sizeof(uint64_Array));
printf("sizeof(float32_Array) = %d \n", sizeof(float32_Array));
printf("sizeof(float64_Array) = %d \n", sizeof(float64_Array));
printf("\n\n");
uint8_Array[0] = '1';
printf("strlen(uint8_Array) = %d \n", strlen(uint8_Array));
printf("\n\n");
struct sData data1;
printf("==~~ sizeof(data1) = %d, add sizeof( ... + ...) = %d ~~== \n", sizeof(data1),
sizeof(data1.uint32_data_1) + sizeof(data1.uint8_data_1) +
sizeof(data1.uint8_data_2) + sizeof(data1.uint32_data_2) +
sizeof(data1.uint16_data_1) + sizeof(data1.uint8_data_3) +
sizeof(data1.uint32_data_3) + sizeof(data1.uint64_data_1)
);
printf("\n\n");
return 1;
}
4.4 c.c文件内容:
#include "1.h"
int c_fun(void)
{
printf("\n== c_fun == \n");
printf("\n============ char min max dig ============\n");
printf("CHAR_MIN( char MinValue ) = 0x%X \n", CHAR_MIN);
printf("CHAR_MAX( char MaxValue ) = 0x%X \n", CHAR_MAX);
printf("\n============ int min max dig ============\n");
printf("INT_MIN( int MinValue ) = 0x%X \n", INT_MIN);
printf("INT_MAX( int MaxValue ) = 0x%X \n", INT_MAX);
printf("\n============ float min max dig ============\n"); // %E
printf("FLT_MIN( float MinValue ) = %f \n", FLT_MIN);
printf("FLT_MAX( float MaxValue ) = %f ( %E )\n", FLT_MAX, FLT_MAX);
printf("FLT_DIG( float PrecisionValue ) = %d \n", FLT_DIG);
printf("\n============ double min max dig ============\n");
printf("DBL_MIN( double MinValue ) = %f \n", DBL_MIN);
printf("DBL_MAX( double MaxValue ) = %f ( %E ) \n", DBL_MAX, DBL_MAX);
printf("DBL_DIG( double PrecisionValue ) = %d \n", DBL_DIG);
unsigned long long FloatConvert = 0;
unsigned long long DoubleConvert = 0;
FloatConvert = (unsigned long long)FLT_MAX;
DoubleConvert = (unsigned long long)DBL_MAX;
printf("FloatConvert = 0x%llX, DoubleConvert = 0x%llX \n\n", FloatConvert, DoubleConvert);
unsigned int x = 0x87654321; // 高位为8,低位为1
unsigned char *p = (unsigned char *)&x;
int i = 0;
// 数据最左为高位,最右为低位
// 小端模式:低地址低位数据,高地址高位数据
// 大端模式:低地址高位数据,高地址低位数据
printf("x = 0x%X \n", x);
for(i = 0; i < 4; i++)
{
printf("0x%X(Addr) = 0x%X \n", (unsigned int)(p + i), p[i]);
}
if(*p == 0x21)
{
printf("Little endian mode!\n\n");
}
else
{
printf("Big endian mode!\n\n");
}
return 1;
}
4.5 Makefile文件内容:
# 目标文件的运行平台: ARM x86 MIPS RISC
#RUN_ARCH := x86
RUN_ARCH := ARM
# 目标文件名,输入任意你想要的执行文件名
TARGET_FILE := TestSizeof_MF
TARGET_PATH := ~/nfs/
# 编译工具
ifeq ($(RUN_ARCH), ARM)
CROSS := /opt/gcc-linaro-arm-linux-gnueabihf-4.7-2012.11-20121123_linux/bin/arm-linux-gnueabihf-
else ifeq ($(RUN_ARCH), x86)
CROSS :=
endif
# CROSS = arm-linux-gnueabihf-
export CC = $(CROSS)gcc
export CXX = $(CROSS)g++
export AR := $(CROSS)ar
export AS := $(CROSS)as
export STRIP := $(CROSS)strip
export CPP = $(CC) -E
export OBJCOPY = $(CROSS)objcopy
export OBJDUMP = $(CROSS)objdump
export NM = $(CROSS)nm
export LD = $(CROSS)ld
# 源文件,自动找所有.c和.cpp文件,并将目标定义为同名.o文件
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))
# 编译参数
LIBS := #-L /Path -lname
LDFLAGS :=
DEFINES :=
INCLUDE := #-I /Path/include/
CFLAGS := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
# 下面的基本上不需要做任何改动了
.PHONY : everything objs clean veryclean rebuild
everything : $(TARGET_FILE)
all : $(TARGET_FILE)
objs : $(OBJS)
rebuild: veryclean everything
clean :
rm -rf *.o $(TARGET_FILE)
veryclean : clean
rm -rf $(TARGET_FILE)
$(TARGET_FILE) : $(OBJS)
$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
# 应用程序需要删掉的编译中间文件
rm -rf *.o
# 拷贝目标文件
cp $(TARGET_FILE) $(TARGET_PATH)
5. 文件内容替换/修改(旧字符串与新字符串大小不等长):
#if 0
程序思路:
1. 先比较新、旧字符串的长度,判断偏移位置的长度与方向
2. 查找目标行,假如找到,则读取目标行往后至文本结束的所有数据到Buffer
3. 替换目标行的数据
4. 将Buffer的数据导入替换完目标行的数据后面
5. 假如旧字符串数据比新字符串数据长,则截断多余文本长度
笔者注:
该程序主要用于工程项目中修改配置文件中的参数,通过匹配配置文件中指定的变量去替换参数。
程序亲测可用,替换的本质是对指定行的字符串替换,字符串的长度不限制,可过长也可过短,
也可不变,因此有一定的局限性。
程序优点:运行过程中不创建新的文本,减少对NandFlash的擦除与进入。
后面过来学习的同学可以对程序进行修改,直到功能满足自己的需求
#endif
# 原文本(程序运行前的)内容
$ cat ChangeFile.txt
[This is a Configuration File]
Parameter_A=0123456789
Parameter_B=0123456789
Parameter_C=0123456789
Parameter_D=0123456789
Parameter_E=0123456789
Parameter_F=0123456789
Parameter_G=0123456789
# 程序运行后的内容
$ cat ChangeFile.txt
[This is a Configuration File]
Parameter_A=0123456789
Parameter_B=0123456789
Parameter_C=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Parameter_D=0123456789
Parameter_E=0123456789
Parameter_F=0123456789
Parameter_G=0123456789
// 程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
// 用于消除警告fgets、fread、ftruncate等函数的编译警告
// 适合喜欢“0 errors, 0 warnings”的同学
char *pEliminateWarn = NULL;
int EliminateWarn = 0;
int OpenFile(char *ChangeFile, unsigned int *FileLength, FILE **fp);
int FileOperation(unsigned int FileLength, FILE *fp, char *OldStr,
char *NewStr, int StrDiffer);
void CloseFile(FILE *fp);
void SetFileLength(char *ChangeFile, unsigned int FileLength, int StrDiffer);
int main(int argc, char *argv[])
{
char ChangeFile[] = "ChangeFile.txt";
char OldStr[] = "Parameter_C=0123456789";
char NewStr[] = "Parameter_C=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int StrDiffer = 0; // OldStr 与 NewStr 之差
FILE *fp = NULL;
unsigned int FileLength = 0;
int ret = 0;
// StrDiffer > 0 说明文本后面的数据需要往前移动,且旧文本比新文本 长,
// 文本末尾需要 截断 StrDiffer 个字符长度;
// StrDiffer < 0 说明文本后面的数据需要往后移动,且旧文本比新文本 短
// 文本末尾需要 增长 StrDiffer 个字符长度;
// StrDiffer = 0 说明文本后面的数据不需要移动,且旧文本与新文本 等长
// 文本末尾 不变。
StrDiffer = strlen(OldStr) - strlen(NewStr);
// 1.打开需要修改的文件
ret = OpenFile(ChangeFile, &FileLength, &fp);
if(ret < 0)
{
return 0;
}
// 2.文件操作
FileOperation(FileLength, fp, OldStr, NewStr, StrDiffer);
// 3.关闭文件
CloseFile(fp);
// 4.如果(OldStr > NewStr),需截断文本末尾的空余,即设置文本长度
if(StrDiffer > 0)
{
SetFileLength(ChangeFile, FileLength, StrDiffer);
}
return 0;
}
int OpenFile(char *ChangeFile, unsigned int *FileLength, FILE **fp)
{
// 打开需要修改的文件
*fp = fopen(ChangeFile, "rwb+");
if(*fp == NULL)
{
printf("open %s error \n", ChangeFile);
return -1;
}
// 将文件指针定位于文件的末尾处,获取文件大小
fseek(*fp, 0, SEEK_END);
*FileLength = ftell(*fp);
printf("FileLength = %dB (%.2fMB) \n", *FileLength,
((double)*FileLength / (1024 * 1024)));
return 1;
}
void CloseFile(FILE *fp)
{
fclose(fp);
}
void SetFileLength(char *ChangeFile, unsigned int FileLength, int StrDiffer)
{
int fd = 0;
unsigned int length = 0;
fd = open(ChangeFile, O_RDWR);
length = FileLength - StrDiffer;
EliminateWarn = ftruncate(fd, length);
printf("==== length = %d === \n", length);
close(fd);
}
int FileOperation(unsigned int FileLength, FILE *fp, char *OldStr,
char *NewStr, int StrDiffer)
{
// 将文件指针指向文件头
fseek(fp, 0, SEEK_SET);
// 进行相应的文件操作
unsigned int RemainFileLength = 0; // 剩余的长度
unsigned int CurrentFileLength = 0; // 当前的长度
unsigned char *RemainFileData = NULL; // 剩下的文本数据
char Array[500]; // 存储文本的行数据
int ArrayLength = 0; // 记录该行数据的长度
while(1)
{
// 1.提取文本的行数据
memset(Array, '\0', sizeof(Array));
if(fgets(Array, sizeof(Array), fp) == NULL)
{
break;
}
// 2.记录文本的行数据长度
ArrayLength = strlen(Array);
// 3.比较该行是否为目标行
if(strncmp(Array, OldStr, ArrayLength - 1) == 0)
{
// 4.定位文本中在OldStr后第1位(即“\n”)的位置
CurrentFileLength = ftell(fp);
// 5.读取出文本中在OldStr后面不需要修改的文本数据 到 RemainFileData
RemainFileLength = FileLength - CurrentFileLength;
RemainFileData = (unsigned char *)malloc(RemainFileLength + 1);
memset(RemainFileData, 0, (RemainFileLength + 1));
EliminateWarn = fread(RemainFileData, sizeof(unsigned char), (RemainFileLength), fp);
// 6.将文件指针指向OldStr前第1位(即“\n”)的位置,将NewStr替换OldStr
memset(Array, '\0', sizeof(Array));
strcpy(Array, NewStr);
fseek(fp, (CurrentFileLength - ArrayLength), SEEK_SET);
fprintf(fp, "%s", Array);
// 7.在文本中NewStr后第1位补上换行符(“\n”)
fwrite("\n", sizeof(unsigned char), 1, fp);
// 8.将RemainFileData中的数据导入到文本NewStr后,并释放RemainFileData
fwrite(RemainFileData, sizeof(unsigned char), RemainFileLength, fp);
free(RemainFileData);
// 9.重新将文件指针定位到一开始的位置
fseek(fp, CurrentFileLength, SEEK_SET);
}
}
return 1;
}
6. 驱动中的文件操作:
6.1 程序部分:
// 笔者对内核态下的文件操作只停留在驱动程序读取文本内容,根据读取配置文件的信息,
// 驱动加载时进行定向运行,但深入的应用并不了解
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/slab.h>
int __init driver_file_init(void)
{
mm_segment_t fs;
struct file *fpRead = NULL, *fpWrite = NULL;
struct kstat *StatRead = NULL;
char *pFileBuf = NULL;
loff_t PosRead = 0, PosWrite = 0;
unsigned long long FileLength = 0;
const char ReadFile[] = "/tmp/ReadFile.txt";
const char WriteFile[] = "/tmp/WriteFile.txt";
printk("driver_file enter \n");
// 1.获取内核态默认的内存地址限制,设置内存地址限制覆盖到用户空间
fs = get_fs();
set_fs(KERNEL_DS);
// 2.获取相应文件的大小
StatRead = (struct kstat *)kmalloc(sizeof(struct kstat), GFP_KERNEL);
memset(StatRead, 0, sizeof(struct kstat));
vfs_stat(ReadFile, StatRead);
FileLength = StatRead->size;
kfree(StatRead);
// 3.打开读、写文件
fpRead = filp_open(ReadFile, O_RDWR | O_CREAT, 0644);
if(IS_ERR(fpRead))
{
printk("Open %s read file error \n", ReadFile);
goto open_read_file_fail;
}
fpWrite = filp_open(WriteFile, O_RDWR | O_CREAT, 0644);
if(IS_ERR(fpWrite))
{
printk("Open %s write file error \n", WriteFile);
goto open_write_file_fail;
}
// 4.清空Buffer
pFileBuf = (char *)kmalloc(FileLength * sizeof(char) + 1, GFP_KERNEL);
memset(pFileBuf, 0, FileLength * sizeof(char) + 1);
// 5.将读文件的内容导入到Buffer,再把Buffer的内容导入到写文件
PosWrite = PosRead = 0;
vfs_read(fpRead, pFileBuf, (FileLength * sizeof(char)), &PosRead);
vfs_write(fpWrite, pFileBuf, (FileLength * sizeof(char)), &PosWrite);
printk(KERN_INFO "==== pFileBuf = %s ==== \n", pFileBuf);
// 6.释放Buffer
kfree(pFileBuf);
// 7.关闭读写文件
filp_close(fpRead, NULL);
filp_close(fpWrite, NULL);
// 8.设置内核态的内存地址限制为默认的内存地址限制
set_fs(fs);
return 0;
open_write_file_fail:
filp_close(fpRead, NULL);
open_read_file_fail:
set_fs(fs);
return -1;
}
void __exit driver_file_exit(void)
{
printk("driver_file exit \n");
}
module_init(driver_file_init);
module_exit(driver_file_exit);
MODULE_LICENSE("GPL");
6.2 对struct kstat结构体的解析:
struct kstat结构体位于 include/linux/stat.h,Linux 3.10.31-LTSI
struct kstat {
u64 ino; // inode number,inode节点号
dev_t dev; // ID of device containing file,文件所在设备的ID
umode_t mode; // protection,保护模式
unsigned int nlink; // number of hard links,链向此文件的连接数(硬连接)
kuid_t uid; // user ID of owner,user id
kgid_t gid; // group ID of owner,group id
dev_t rdev; // device ID (if special file),设备号,针对设备文件
loff_t size; // total size, in bytes,文件大小,字节为单位
struct timespec atime; // time of last access,最近访问时间
struct timespec mtime; // time of last modification,最近修改时间
struct timespec ctime; // time of last status change,创建时间
unsigned long blksize; // blocksize for filesystem I/O,系统块的大小
unsigned long long blocks; // number of blocks allocated,文件所占块数
};
7. 常规操作中的文件操作:
int Temp;
// 将文件指针定位到文件头
fseek(fp, 0, SEEK_SET);
while(1)
{
// 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
Temp = fgetc(fp);
// 该判断只适合fgetc
if(Temp == EOF)
{
// 把文件流里的所有未写出数据立刻写出
fflush(fp);
printf("\n");
printf("File point have reached the end of file Or Read error \n");
sleep(5);
}
// 该判断具有通用性,测试一个文件流的文件尾标识
if(feof(fp))
{
// 把文件流里的所有未写出数据立刻写出
fflush(fp);
printf("\n");
printf("File point have reached the end of file \n");
sleep(5);
}
printf("%c", Temp);
}
8. 获取各个挂载点的存储信息操作:
8.1 程序部分:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/vfs.h> // statfs、fstatfs函数
#include <mntent.h> // setmntent、getmntent、endmntent函数
char MountPath[] = "/proc/mounts"; // proc系统中的mount文件
struct sStoragreSize
{
// 容量、使用、可用,单位为 KB
unsigned int Size_KB;
unsigned int Used_KB;
unsigned int Available_KB;
// 挂载标志位:为0,没有挂载;为1,有挂载。
unsigned char MountFlag;
};
void ReadStoragreSize(char *DevicePath, struct sStoragreSize *pStoragre)
{
FILE *fpMount = NULL;
struct mntent *mnt = NULL;
struct statfs diskInfo;
pStoragre->MountFlag = 0;
// 打开proc系统中的mount文件
fpMount = setmntent(MountPath, "r");
if(fpMount == NULL)
{
printf("== Open %s error == \n", MountPath);
return;
}
// 判断是否有挂载相应的设备,并标记有挂载的设备
while(1)
{
mnt = getmntent(fpMount);
if(strncmp(mnt->mnt_dir, DevicePath, (sizeof(DevicePath)) - 1) == 0)
{
pStoragre->MountFlag = 1;
break;
}
}
endmntent(fpMount);
if(pStoragre->MountFlag)
{
memset(&diskInfo, 0, sizeof(struct statfs));
statfs(DevicePath, &diskInfo);
// 一定要强制转化为unsigned long long(64位),若地址超过4G,会导致数据溢出
// 右移10位是为了方便存储数据,只需要用unsigned int(32位)就可以存储
pStoragre->Size_KB = ((unsigned long long)diskInfo.f_bsize *
(unsigned long long)diskInfo.f_blocks) >> 10;
pStoragre->Used_KB = ((unsigned long long)diskInfo.f_bsize *
(unsigned long long)(diskInfo.f_blocks -
diskInfo.f_bfree)) >> 10;
pStoragre->Available_KB = ((unsigned long long)diskInfo.f_bsize *
(unsigned long long)diskInfo.f_bavail) >> 10;
}
else
{
pStoragre->Size_KB = pStoragre->Used_KB = pStoragre->Available_KB = 0;
}
}
int main(int argc, char *argv[])
{
int i = 0;
char DevicePath[15];
struct sStoragreSize Storagre;
char MachinePath[] = "/home"; // 机身存储的挂载路径
char SDCardPath[] = "/mnt/sdcard"; // SD卡的挂载路径
char UdiskPath[] = "/mnt/udisk"; // U盘的挂载路径
for(i = 0; i < 3; i++)
{
memset(DevicePath, 0, sizeof(DevicePath));
memset(&Storagre, 0, sizeof(struct sStoragreSize));
switch(i)
{
case 0:
memcpy(DevicePath, MachinePath, sizeof(MachinePath));
break;
case 1:
memcpy(DevicePath, SDCardPath, sizeof(SDCardPath));
break;
case 2:
memcpy(DevicePath, UdiskPath, sizeof(UdiskPath));
break;
}
ReadStoragreSize(DevicePath, &Storagre);
if(Storagre.MountFlag)
{
printf("DevicePath = %s \n", DevicePath);
printf("Size = %.3f GB \n", ((double)Storagre.Size_KB / (1024 * 1024)));
printf("Used = %.3f GB \n", ((double)Storagre.Used_KB / (1024 * 1024)));
printf("Available = %.3f GB \n\n", ((double)Storagre.Available_KB /
(1024 * 1024)));
}
}
return 0;
}
8.2 对struct statfs结构体的解析:
// struct statfs结构体位于 /usr/include/i386-linux-gnu/bits/statfs.h
struct statfs
{
__fsword_t f_type; // 文件系统类型
__fsword_t f_bsize; // 经过优化的传输块大小
#ifndef __USE_FILE_OFFSET64
__fsblkcnt_t f_blocks; // 文件系统数据块总数
__fsblkcnt_t f_bfree; // 可用块数
__fsblkcnt_t f_bavail; // 非超级用户可获取的块数
__fsfilcnt_t f_files; // 文件结点总数
__fsfilcnt_t f_ffree; // 可用文件结点数
#else
__fsblkcnt64_t f_blocks; // 文件系统数据块总数
__fsblkcnt64_t f_bfree; // 可用块数
__fsblkcnt64_t f_bavail; // 非超级用户可获取的块数
__fsfilcnt64_t f_files; // 文件结点总数
__fsfilcnt64_t f_ffree; // 可用文件结点数
#endif
__fsid_t f_fsid; // 文件系统标识
__fsword_t f_namelen; // 文件名的最大长度
__fsword_t f_frsize;
__fsword_t f_flags;
__fsword_t f_spare[4]; // spare for later
};
8.3 对struct stat结构体的解析:
// struct stat结构体位于 /usr/include/i386-linux-gnu/asm/stat.h
#ifdef __i386__
struct stat {
unsigned long st_dev; // 文件的设备编号
unsigned long st_ino; // 节点
unsigned short st_mode; // 文件的类型和存取的读写执行权限
unsigned short st_nlink; // 连到该文件的硬连接数目,刚建立的文件值为1
unsigned short st_uid; // 用户ID
unsigned short st_gid; // 组ID
unsigned long st_rdev; // (设备类型)若此文件为设备文件,则为其设备编号
unsigned long st_size; // 文件字节数(文件大小)
unsigned long st_blksize; // 块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; // 文件所占块数
unsigned long st_atime; // 最后一次访问时间
unsigned long st_atime_nsec; //
unsigned long st_mtime; // 最后一次修改时间
unsigned long st_mtime_nsec; //
unsigned long st_ctime; // 最后一次改变时间(指属性)
unsigned long st_ctime_nsec; //
unsigned long __unused4; //
unsigned long __unused5; //
};
#else /* __i386__ */
struct stat {
__kernel_ulong_t st_dev;
__kernel_ulong_t st_ino;
__kernel_ulong_t st_nlink;
unsigned int st_mode;
unsigned int st_uid;
unsigned int st_gid;
unsigned int __pad0;
__kernel_ulong_t st_rdev;
__kernel_long_t st_size;
__kernel_long_t st_blksize;
__kernel_long_t st_blocks; // 分配的512字节块数。
__kernel_ulong_t st_atime;
__kernel_ulong_t st_atime_nsec;
__kernel_ulong_t st_mtime;
__kernel_ulong_t st_mtime_nsec;
__kernel_ulong_t st_ctime;
__kernel_ulong_t st_ctime_nsec;
__kernel_long_t __unused[3];
};
#endif
8.4 stat、fstat和lstat函数
stat、fstat和lstat函数:
https://www.cnblogs.com/xj626852095/p/3648237.html
8.5 对比内核态struct kstat结构体和用户态struct stat结构体
# 共同点:
xx_ino // inode节点号
xx_dev // 文件的设备ID
xx_mode // 文件的类型和存取的读写执行权限
xx_nlink // 连到该文件的硬连接数
xx_uid // 用户ID
xx_gid // 组ID
xx_rdev // 若此文件为设备文件,则为其设备编号
xx_size // 文件大小,字节为单位
xx_blksize // 系统块大小(文件系统的I/O 缓冲区大小)
xx_blocks // 文件所占块数
xx_atime // 最近访问(access)时间,包含sec和nsec
xx_mtime // 最近修改(modify)时间,包含sec和nsec
xx_ctime // 创建(change)时间,包含sec和nsec
# 不同点:
__unused_xx // 没有使用的
# 总结:
基本上是一样的……
9. 目录操作:待更中……
// 《Linux程序设计 第4版》,Neil Matthew、Richard Stones 著 的第3章与第4章
// 3.7 C语言文件和目录维护
// chmod 系统调用
int chmod(const char *path, mode_t mode);
// chown 系统调用
int chown(const char *path, uid_t owner, gid_t group);
// unlink、link和symlink系统调用
int unlink(const char *path);
int link(const char *path1, const char *path2);
int symlink(const char *path1, const char *path2);
// mkdir和rmdir系统调用
int mkdir(const char *path, mode_t mode);
int rmdir(const char *path);
// chdir系统调用和getcwd函数
int chdir(const char *path);
char *getcwd(char *buf, size_t size);
// 3.8 C语言文件和目录维护
// opendir函数
DIR *opendir(const char *name);
// readdir函数
struct dirent *readdir(DIR *dirp);
// telldir函数
long int telldir(DIR *dirp, long int loc);
// seekdir函数
void seekdir(DIR *dirp);
// closedir函数
int closedir(DIR *dirp);
// 3.9 C语言错误处理
// strerror函数
char *strerror(int errnum);
// perror函数
void perror(const char *s);
// 3.10 /proc文件系统
// 3.11 C语言fcntl和mmap函数
int fcntl(int fildes, int cmd);
int fcntl(int fildes, int cmd, long arg);
void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
int msync(void *addr, size_t len, int flags);
int munmap(void *addr, size_t len);
// 4
// 4.1 int main(int argc, char *argv[])
// 4.1.1 getopt函数
int getopt(int argc, char *const argv[], const char *optstring);
extern char *optarg;
extern int optind, opterr, optopt;
// 4.1.1 getopt_long函数
int getopt_long();
// 4.2 环境变量
char *getenv(const char *name);
int putenv(const char *string);
// 4.2.2 environ变量
extern char **environ;
// 4.3 时间和日期
time_t time(time_t *tloc);
double difftime(time_t timel, time_t time2);
struct tm *gmtime(const time_t timeval);
struct tm *localtime(const time_t *timeval);
time_t mktime(struct tm *timeptr);
char *asctime(const struct tm *timeptr);
char *ctime(const time_t *timeval);
size_t strftime(char *s, size_t maxsize, const char *format, struct tm *timeptr);
char *strptime(const char *buf, const char *format, struct tm *timeptr);
// 4.4 临时文件
char *tmpnam(char *s);
FILE *tmpfile(void);
char *mktemp(char *template);
int mkstemp(char *template);
// 4.5 用户信息
uid_t getuid(void);
char *getlogin(void);
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
void endpwent(void);
struct passwd *getpwent(void);
void setpwent(void);
uid_t geteuid(void);
gid_t getgid(void);
gid_t getegid(void);
int setuid(uid_t uid);
int setgid(gid_t gid);
// 4.6 主机信息
int gethostname(char *name, size_t namelen);
int uname(struct utsname *name);
long gethostid(void);
// 4.7 日志
void syslog(int priority, const char *message, arguments...);
void closelog(void);
void openlog(const char *ident, int logopt, int facility);
int setlogmask(int maskpri);
pid_t getpid(void);
pid_t getppid(void);
// 4.8 资源和限制(硬件的限制、系统策略的限制、具体实现的限制)
int getpriority(int which, id_t who); // 优先级
int setpriority(int which, id_t who, int priority);
int getrlimit(int resource, struct rlimit *r_limit); // 限制
int setrlimit(int resource, const struct rlimit *r_limit);
int getrusage(int who, struct rusage *r_usage); // 资源信息
10. 时间记录:
struct timeval TimeVal_1, TimeVal_2;
gettimeofday(&TimeVal_1, NULL);
// ***** 一顿操作 1 *****
// ***** 一顿操作 2 *****
// ***** 一顿操作 3 *****
gettimeofday(&TimeVal_2, NULL);
double Time1_s = (TimeVal_1.tv_sec) + ((double)TimeVal_1.tv_usec / (1000 * 1000));
double Time2_s = (TimeVal_2.tv_sec) + ((double)TimeVal_2.tv_usec / (1000 * 1000));
double Time1_ms = (TimeVal_1.tv_sec * 1000) + ((double)TimeVal_1.tv_usec / 1000);
double Time2_ms = (TimeVal_2.tv_sec * 1000) + ((double)TimeVal_2.tv_usec / 1000);
printf("Running %.2f min (%.2fs %.1f ms) \n\n", (Time2_s - Time1_s) / 60,
(Time2_s - Time1_s), (Time2_ms - Time1_ms));