C语言文件操作,linux文件操作,文件描述符,linux下一切皆文件,缓冲区,重定向

目录

C语言文件操作

如何打开文件以及打开文件方式

读写文件

关闭文件

Linux系统下的文件操作

open

宏标志位

write,read,close,lseek接口

什么是当前路径?

linux下一切皆文件

文件描述符

文件描述符排序

C语言文件操作它们究竟是如何找到文件的呢?

重定向

缓冲区


首先我们抛出一个问题——如何进行文件操作?最普遍的方式就是使用我们的高级语言如C,C++,java这样的语言级别的接口来辅助我们操作文件,对文件进行读写操作;那语言级别的接口是怎么进行文件操作的呢?这些语言级别的文件操作为什么可以做到对文件的增删查改呢?

我们先看一下,我们之前C语言阶段学习过的文件操作;

C语言文件操作

我们要进行文件操作首先要做的就是打开我们的文件;那如何打开文件呢?

如何打开文件以及打开文件方式

我们可以使用(常用)fopen这个接口来打开我们的文件,path是我们文件的路径名字,mode是打开这个文件的方式有很多不同的选项;

 文件路径:我们的路径可以是相对路径也可以是绝对路径,这里的相对路径相对的是我们可执行程序运行变成进程时所在的目录;

选项:我们的选项大致有6个,w,w+,r,r+,a,a+,这六个选项代表着我们的6种不同的打开文件的方式,下面的对这六种种方式的简介:(这是man手册中的官方简介)

下面是gpt对这6个选项的总结:

1. "r"(只读模式):用于只读操作。如果文件不存在,则打开操作失败,并返回NULL。如果成功打开文件,则文件指针会被放置在文件的开头。

2. "w"(写入模式):用于写入操作。如果文件不存在,则创建一个新文件。如果文件已存在,则会清空文件中的内容。文件指针会被放置在文件的开头。

3. "a"(追加模式):用于追加操作。如果文件不存在,则创建一个新文件。如果文件已存在,则文件指针会被放置在文件的末尾。新写入的内容会追加到文件的末尾,不会清空原有内容。

这三种基本的打开模式还可以与"+"符号结合使用,表示具有读写权限:

1. "r+"(读写模式):用于读写操作。文件指针会被放置在文件的开头。可以对文件进行读取、修改和写入操作。

2. "w+"(读写模式):用于读写操作。如果文件不存在,则创建一个新文件。如果文件已存在,则会清空文件中的内容。文件指针会被放置在文件的开头。可以对文件进行读取、修改和写入操作。

3. "a+"(读写模式):用于读写操作。如果文件不存在,则创建一个新文件。如果文件已存在,则文件指针会被放置在文件的末尾。可以对文件进行读取、修改和写入操作。

需要注意的是,在使用读写模式时,要特别小心文件指针的位置以避免错误的读取或写入操作。使用`fseek()`函数可控制文件指针的位置。

我再用我的方式总结一下:

r和w代表的就是只读只写 流(也可以看作我们打字时候的光标位置)位于文件开头,不同的是w会清空文件所有内容,并且当文件不存在时会自动创建一个文件提供给用户,而r不具备创建和消除的功能它只能读而且是从开头读;

r+和w+的区别与r和w的区别一样(创建和消除的区别)但是我们需要注意的是r+和w+它们在读写的时候流是会随着读写的位置移动的所以我们在写或者读的时候要注意流的位置,就比如我们使用r+我们先写入了一段数据我们想要读我们这个文件中所有数据的时候我们一定要把流的位置移动到文件开头不然读到的数据就是我们写入数据的后面的位置因为我们写入数据到了这里所以流的位置变了不是在文件开头了;

a和a+是以追加的形式打开文件文件不存在则创建存在则将流放置在文件结尾,a+可以读文件而a不可以;

接下来我们说说读写文件的函数:

读写文件

读:fscanf,fread, fgets

写:fprintf, fwirte, fputs

这些读写的函数就不多进行描述了在man手册中都可以清楚明了的看到它们的内容以及如何使用;

我提供一个我的记忆方式:就是和我们C语言中的gets,scanf,puts,printf一起记忆,scanf和printf只是在参数前面多了一个流参数,而fputs是在puts参数后面多了一个流,而fgets只是在gets函数的参数后面多了一个存放文件中数据的数组大小和流;(因为fgets字符串类型函数所以我们只需要提供数组大小即可fgets会自动帮我们在数组末尾加\0的)

下面是读写文件的操作示例:

关闭文件

我们打开了文件之后自然需要关闭文件,这样可以保证文件的安全性等;

fclose就是关闭文件的函数,使用也非常简单把FILE类型的指针给fclose就可以关闭文件了;

上面的就是C语言文件操作的简单复习了;

Linux系统下的文件操作

接下来我们进入重点,为什么高级语言可以对文件进行操作呢?它到底是怎么做到的?

首先,我们回顾一下我们之前的对操作系统的理解,操作系统是为进程提供安全稳定环境的软件,它负责管理我们计算机中的进程,我们使用C语言编写程序的时候所用到的软件以及库函数其实都是操作系统提供的,而我们的C语言的库函数就是在系统接口的基础上对库函数进行封装带上一系列的自定义行为,这样既可以让我们的代码跨平台服务也简化了我们的语言学习与使用;

既然C语言库函数的底层是系统调用那么C语言文件操作库函数的底层的系统调用是什么样的呢?我们下面来看看在linux下C语言的文件操作的系统调用接口;

open

对应着库函数中的fopen,open有两类函数一类是三个参数的一类是两个的;两个参数的open函数一般是只用来读文件,三个参数的函数则是我们常用的函数;

open的三个参数

第一个参数代表了我们的文件路径

第二个参数是打开文件的选项,选项常用的有:

O_RDONLY:只读

O_WRONLY:只写

O_RDWR:可读可写

上面这三个选项必须有且只有一个选项在参数中,这些参数是不是和C语言文件操作函数中的r w +选项类似,是的!因为上面的参数就是C语言选项的底层;再加上下面这几个选项就可以成功的组建出C语言文件操作函数的打开功

O_CREAT:文件不存在时通过前面的路径创建出一个文件

O_APPEND:在文件末尾追加

O_TRUNC: 清空文件

但是这么多的选项我们怎么样才能在一个参数中使用呢?这个时候就是宏标志位登场的时候啦!我们首先看看上面的选项是不是都是大写?大写的字符在C语言中一般是不是在宏定义的时候经常使用呢?

那什么是标志位?顾名思义就是位上不同的信号,一个int类型数据有32个bit位在每个位上可以有0,1两种信号,所以我们可以使用一个参数来传递32种不同的选项,某位上为0则是否为1则代表选项成立;

宏标志位

我们可以通过下面这个小程序来理解宏标志位:

#include<stdio.h>
通过宏标志位来传递参数
#define FIRST 0x1
#define SECOND 0x2
#define THIRD 0x4void fun(int macro)
{if(macro&FIRST)printf("这是功能1\n");if(macro&SECOND)printf("这是功能2\n");if(macro&THIRD)printf("这是功能3\n");
}int main()
{fun(1);//0001printf("-------------------\n");fun(2);//0010printf("-------------------\n");fun(3);//0011printf("-------------------\n");fun(4);//0100printf("-------------------\n");fun(5);//0101return 0;
}

现象: 

可以看到我们通过宏标志位的传递实现的多种不同功能的组合使用;

接下来我们看第三个参数

第三个参数是用来修改我们文件权限的,这也是为什么我们常使用的是三个参数的open函数的原因;当我们不使用低三个参数的时候我们会发现:

为了避免这种情况我们使用第三个参数;

我们通过传递权限的八进制数来修改权限,这里可以看我前面写的博客来理解

Linux操作系统——权限-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_75260318/article/details/132946078?spm=1001.2014.3001.5501所以我们传递需要这样来操作

 这就是open函数的使用;

write,read,close,lseek接口

write:

第一个参数是文件描述符,就是我们open打开后的返回值具体是什么,我们下面的文件描述符会详细讲解;

第二个参数就是我们的写入数据的内容,我们可以传数组,也可以传字符串;

最后一个参数就是字节数,是我们传递的数据的字节数;

write接口会向我们的内核中写入我们的buf数据,再通过内核写入到fd文件中;

read:

它的参数和上面的write没有什么不同,不过是将数据从fd文件中读取count个字节的数据到buf中;

第二个参数是我们接受数据的指针,指向存储数据的区域;

close:

关闭打开的fd文件

lseek:

用来调整文件流的位置(光标位置)

下面是我通过chat-gpt获得的关于lseek的信息:

- `fd` 是已经打开的文件的文件描述符,可以使用 `open` 函数获取。
- `offset` 是要移动的偏移量,可以为正(向文件尾部移动)或负(向文件开头移动)。
- `whence` 是指定偏移量是相对于文件开头、当前位置还是文件尾的标志。
  - 如果 `whence` 为 `SEEK_SET`,`offset` 表示文件开始位置的偏移量。
  - 如果 `whence` 为 `SEEK_CUR`,`offset` 表示当前的位置偏移量。
  - 如果 `whence` 为 `SEEK_END`,`offset` 表示文件末尾的偏移量。

什么是当前路径?

为什么我们可以把文件创建在进程当前路径中,当前路径到底是什么意思?我们在运行可执行程序后程序变成了进程,而进程在我们之前的学习中,我们知道进程有进程的pcb结构体,这个结构体中存放了进程的各种信息,那么我们运行进程时所在的路径也会被记录下来,这个路径就是当前进程所在的路径,也就是当前路径的根本意义;

下面我们通过实际现象来证明我们思路:

由此我们可以清楚的明白所谓的当前路径就是存储在进程的pcb结构体中的进程信息,我们只要通过查找进程的结构体就可以找到我们的当前路径; 

我们讲完了open的参数接下来我们得看看open的返回值,我们可以惊讶的看到open这个系统调用的返回值居然是int类型的,这说明它只返回了一些数字,而返回数字就和我们前面C语言封装函数fopen的接口有很大差别了,C语言接口返回的是一个指针类型,至少还是可以指向某个大的存储区域的,而我们这里简简单单返回一个int究竟做了什么呢?我们就要引入一个新的知识——文件描述符;

linux下一切皆文件

我们先回顾一下Linux下一切皆文件这个概念;之前的学习中我们模模糊糊知道了linux下一切皆文件,但究竟什么叫一切皆文件呢?我们的计算机是由硬件和软件组成对于我们的操作系统,操作系统把硬件一般叫做外设,而操作系统需要管理外设,来为我们用户提供服务;如何管理呢?先描述,在组织;外设无法直接与操作系统交流,于是外设的信息形成了一个个结构体,操作系统使用不同的数据结构将这些结构体组织起来,并对结构体中的信息进程增删查改来管理外设;而要对这些外设进行访问肯定要使用到open,write,read这样的接口;

通过上面的解释我们就可以很好的理解linux下一切皆文件的道理;

文件描述符

上面讲到open系统接口的返回值就是文件描述符,并且write,read,close,lseek这样的系统接口的参数都需要用到文件描述符,那么文件描述符究竟是什么;之前我们说linux下一切皆文件,所以一切的文件被打开的文件都会有包含文件属性结构体file,file中存储了文件的相关信息,

而这些文件的结构体又由打开它们的进程进行管理,进程的pcb结构体中有一个files*的指针这个指针指向了文件结构体(这个结构体装的是进程中被打开的所有文件的信息),

在files_struct结构体中又有一个file*的fd_array指针数组(也叫文件映射表)这个数组装的就是file结构体的指针,这些指针就是指向被打开文件的;

文件描述符排序

而我们的linux进程默认打开0标准输入(对应外设键盘),1标准输出(显示器),2标准错误(显示器)三个文件(外设),而数组的0,1,2三个位置就是指向这三个文件的;在C语言中0,1,2被封装成了stdin,stdout,stderr;剩下的我们自己打开的文件会跟随我们的文件的打开顺序来分配数组下标;

需要注意的是:

如果我们关闭了文件,那么文件所在的fd_array数组位置将会制空,为接下来的打开文件让开位置;

了解了这些,现在我们总算是知道了文件描述符fd究竟是什么了,文件描述符其实就是数组下标而已,我们可以通过数组下标找到,数组中的指针,通过指针找到文件信息结构体,从而找到文件;

C语言中的文件描述符fileno:由此可见C语言的FILE结构体中一定也封装了fd;事情也是如此,C语言的FILE结构体中有fileno这样的成员变量,这个成员变量就是我们系统接口中的文件描述符;

C语言文件操作它们究竟是如何找到文件的呢?

如fopen这样的函数,首先它肯定是先调用我们的系统接口open,open接口又通过进程的结构体中的files指针找到files_struct结构体,在结构体中的fd_array数组中对应封装的fileno编号,通过编号对应的数组下标找到数组中指针指向的文件结构体,从而找到文件,打开文件;这就是C语言的文件操作找到文件的全部过程;(对照上面的图片分析效果更佳)

重定向

由于linux进程默认打开了三个文件,所以我们的输入输出默认也会从我们的键盘和屏幕读写;而重定向的意思就是改变默认的读写文件;就比如我们的cat AA.txt>BB.txt,这个指令就是把AA.txt中的数据输出到BB.txt文件中,本来cat指令默认是输出到显示器上的但是由于重定向>使得输出的方向变了;

我们可以使用dup2函数进行重定向:

这个函数的作用是将fd_array文件映射表上的oldfd下标位置的指针拷贝给newfd位置的指针;

示例

重定向有输出重定向> 输入重定向<  追加重定向>> 这些重定向;(这里就不一一实现了)

缓冲区

缓冲区这一概念我们一定都很熟悉,但是我以前只知道如果输入或者输出的字符没有打印完全的话,就刷新一下缓冲区就好了,比如使用fflush(stdout)或者使用getchar读取输入缓冲区中的\n字符;这样的理解有些片面,所以现在我们来理想的面对一下缓冲区;

缓冲区是什么?

其实缓冲区就是一段内存空间,用来存储我们的数据;

缓冲区有什么用?

当没有缓冲区的时候,我们的内核之间向外设写入,每次有数据就直接写入外设这样的写透模式WT,伴随着多次的交互,有交互就一定需要外设的准备,准备就一定需要消耗时间,并且我们的各级内存之间的运行速度差距很大,消耗的时间一定更多,而多次交互就要多次消耗时间(虽然这个时间在我们看来很多但对应计算机来说太浪费时间了),为了减少内核和外设的交互次数,就出现了缓冲区这一概念;将数据放入缓冲区当缓冲区满了或者满足刷新策略的时候就刷新缓存区将数据输出;这样一来交互的次数大大减少了!

缓冲区在哪?

缓冲区其实就是一段定义好的buffer数组,我们的C语言库中有着buffer的实现,如我们使用scanf函数的时候就是先把数据写入buffer中当遇到换行时就会和内核交互将数据刷新入内核,内核也有自己的缓冲区也有自己的刷新模式;在C语言中向显示中刷新是行刷新策略向磁盘刷新一般是满刷新策略;所以用户级缓冲区在用户使用的高级语言的库中实现,而内核级缓冲区在内核的代码中实现;

模拟实现缓冲区

#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#define BUFFER_SIZE 1024
#define BUFFER_SIZE 10typedef struct MyFILE
{char _buffer[BUFFER_SIZE];int _end;int _fd;int _end;//指向最后一个数据后面一个也可以当作是sizeint _fd;//文件描述符
}file;file* myfopen(const char*filename,const char* mode)
@@ -68,56 +68,71 @@ void myfflush(file*stream)}write(stream->_fd,stream->_buffer,stream->_end);//载入内核缓冲区syncfs(stream->_fd);//载入磁盘stream->_end=0;memset(stream->_buffer,0,sizeof(stream->_buffer));
}void myfputs(const char*str,file*stream)
{file*fp=stream;if(fp->_end+strlen(str)>=BUFFER_SIZE)if(fp->_end+strlen(str)>=BUFFER_SIZE)//满刷新判断,这是所以文件都需要满足的{//如果满了直接就载入内存了myfflush(fp);fp->_end=0;memset(fp->_buffer,0,BUFFER_SIZE);fprintf(stderr,"范围那里错了\n");while(strlen(str)>=BUFFER_SIZE)//如果刷新了之后还是满了的话{memcpy(fp->_buffer,str,BUFFER_SIZE-1);fp->_end=BUFFER_SIZE-1;stream->_buffer[fp->_end]='\0';str=str+BUFFER_SIZE-1;myfflush(fp);}//不再满了之后strcpy(fp->_buffer,str);fp->_end=strlen(str);//debug//printf("%s\n",fp->_buffer);}else{//不满则直接放入缓冲区strcpy(fp->_buffer+fp->_end,str); fp->_end+=strlen(str);}strcpy(fp->_buffer+fp->_end,str); fp->_end+=strlen(str);if(fp->_fd==1||fp->_fd==2)//如果是标准输出和标准错误时的刷新策略{ //标准输出//标准输出和标准错误for(int i=strlen(str)-1;i>=0;i--){{//从后往前寻找如果有\n就刷新if(str[i]=='\n'){write(fp->_fd,fp->_buffer,i+1);if(str[i+1]!='\0')myfflush(fp);if(str[i+1]!='\0')//把\n后面没有刷新的重新放入缓冲区{fprintf(stderr,"我遇到\n");strcpy(fp->_buffer,fp->_buffer+(i+1));strcpy(fp->_buffer,str+i+1);fp->_end=strlen(fp->_buffer);}return;}}}
}void myfclose(file *stream)
{myfflush(stream);close(stream->_fd);
}
int main()
{close(1);file *fp=myfopen("tmp.txt","w");fprintf(stderr,"%d\n",fp->_fd);myfputs("hello myfputs\n",fp);printf("fp :%d\n",fp->_fd);char str[]="12345678912345678910\n";myfputs(str,fp);myfclose(fp);return 0;
}

这是我模拟实现缓冲区的代码,可以辅助理解;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/721288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【音视频开发好书推荐1】《RTC程序设计:实时音视频权威指南》

1、WebRTC概述 WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个由Google发起的实时音视频通讯C开源库&#xff0c;其提供了音视频采集、编码、网络传输&#xff0c;解码显示等一整套音视频解决方案&#xff0c;我们可以通过该开源库快速地构建出一个音视频通…

牛客练习赛122

D:圆 正着求删除的最小代价不好做&#xff0c;采用逆向思维&#xff0c;求选择一些不相交的线段使得构成一个圆的代价尽量大&#xff0c;最后答案就是所有线段权值之和减去最大代价。 那么如何求这个最大代价呢&#xff1f;显然区间DP 老套路&#xff1a;破环成链&#xff0…

详解Java中集合的List接口实现的ArrayList方法 | Set接口实现的HashSet方法

集合的概念 当我们需要保存一组一样&#xff08;类型相同&#xff09;的元素的时候&#xff0c;我们应该使用一个容器来存储&#xff0c;数组就是这样一个容器。 ● 数组的特点&#xff1a; 数组是一组数据类型相同的元素集合&#xff1b; 创建数组时&#xff0c;必须给定…

Discuz IIS上传附件大于28M失败报错Upload Failed.修改maxAllowedContentLength(图文教程)

下图&#xff1a;Discuz X3.5的系统信息&#xff0c;上传许可为1024MB(1GB) 论坛为局域网论坛&#xff0c;仅供内部同事交流使用&#xff01; 使用官方最新的Discuz! X3.5 Release 20231221 UTF-8 下图&#xff1a;选择上传附件&#xff08;提示可以最大上传100M&#xff09;…

【Python】新手入门(2):避免将关键字作为标识符

Python新手入门&#xff08;2&#xff09;&#xff1a;避免将关键字作为标识符 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1…

保护模式笔记九 中断门和IDT(中断描述符表)

段选择子&#xff1a; 先直观认识一下GDT和段选择子在逻辑地址转换为线性地址中的作用&#xff0c;例如&#xff1a; 给出逻辑地址&#xff1a;21h:12345678h&#xff0c;需要将其转换为线性地址 a. 选择子SEL21h0000000000100 0 01b&#xff0c;他代表的意思是&#xff1a…

Ruoyi框架上传文件

axios资料&#xff1a;axios中文文档|axios中文网 | axios axiosjson 默认情况下&#xff0c;axios将JavaScript对象序列化为JSON。 submit(data) {if (data && this.definitionId) {// 启动流程并将表单数据加入流程变量startProcess(this.definitionId, JSON.string…

数据可视化原理-腾讯-3D热力图

在做数据分析类的产品功能设计时&#xff0c;经常用到可视化方式&#xff0c;挖掘数据价值&#xff0c;表达数据的内在规律与特征展示给客户。 可是作为一个产品经理&#xff0c;&#xff08;1&#xff09;如果不能够掌握各类可视化图形的含义&#xff0c;就不知道哪类数据该用…

代码随想录刷题笔记-Day29

1. N皇后 51. N 皇后https://leetcode.cn/problems/n-queens/ 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数…

【Sql Server】C#通过拼接代码的方式组合添加sql语句,会出现那些情况,参数化的作用

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

JavaWeb JSP

JSP&#xff08;Java Server Page&#xff09;是J2EE的功能模块&#xff0c;是Java服务器页面&#xff0c;由Web服务器执行&#xff0c;作用就是降低动态网页开发难度&#xff0c;将Java代码与HTML分离&#xff0c;降低开发难度&#xff0c;本质就是Servlet。 Servlet的缺点&a…

pandas数据分析42——读取和写入stata和spss的数据格式

python就是胶水语言&#xff0c;啥文件基本都能读取&#xff0c;而且pandas作为数据分析最好用的包&#xff0c;其功能自然也很多&#xff0c;可以读取各种数据文件。 本次就来演示一下怎么读取stata文件&#xff0c;和spss文件&#xff0c;他们不仅储存了数据和变量&#xff…

C++--机器人的运动范围

目录 1. 题目 2. 思路 3. C代码测试 4. 测试结果 1. 题目 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动&#xff0c;每一次只能向左&#xff0c;右&#xff0c;上&#xff0c;下四个方向移动一格&#xff0c;但是不能进入行坐标和列坐标的数位之和大于k的格…

如何应对IT服务交付中的问题?

如何应对IT服务交付中的问题&#xff1f; 按需交付服务的挑战IT服务体系的复杂性恶性循环的形成学会洞察的重要性书籍简介参与方式 按需交付服务的挑战 一致性、可靠性、安全性、隐私性和成本效益的平衡&#xff1a;成功的按需交付服务需要满足这些要求&#xff0c;这需要服务…

html样式排版

<template><div class"box"><div class"header">头部</div><div class"main"><div class"left">菜单</div><div class"right"><div class"right-contentr"&g…

【C++】list模拟实现+反向迭代器

list模拟实现 list定义list用法list iterator的使用begin() end()rbegin()rend() reverse()sort()merge()unique()remove()splice() list模拟实现struct和class的区别list三个类模板默认成员函数构造函数拷贝构造函数赋值运算符重载析构函数 数据修改操作push_back()push_fron…

正信法律:借了钱找不到人怎么办

当一个人借出钱财之后&#xff0c;发现借款人像蒸发了一般无影无踪&#xff0c;这无疑是一件让人头疼的事情。面对这种情况&#xff0c;我们需要采取明智的措施来保护自己的权益。 尽量联络借款人。可以通过电话、短信、电子邮件或社交媒体尝试联系&#xff0c;同时也可以询问共…

界面控件DevExpress WinForms 2024产品路线图预览(一)

DevExpress WinForm拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForm能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜任…

excel数值无法左对齐

右键&#xff0c;单元格格式 修改为常规 解决

【力扣精选算法100道】——判定是否互为字符重排(哈希专题)

目录 &#x1f6a9;了解题意 &#x1f6a9;算法原理 第一种&#xff1a;排序 第二种方法&#xff1a;哈希 &#x1f6a9;实现代码 面试题 01.02. 判定是否互为字符重排 - 力扣&#xff08;LeetCode&#xff09; &#x1f6a9;了解题意 我们输入俩个字符串&#xff0c;当俩…