在本文中,您将会了解到关于PHP读写文件高并发处理操作实例详解的新资讯,同时我们还将为您解释php并发读写文件的相关在本文中,我们将带你探索PHP读写文件高并发处理操作实例详解的奥秘,分析php并发读
在本文中,您将会了解到关于PHP读写文件高并发处理操作实例详解的新资讯,同时我们还将为您解释php 并发读写文件的相关在本文中,我们将带你探索PHP读写文件高并发处理操作实例详解的奥秘,分析php 并发读写文件的特点,并给出一些关于php 中的信号处理操作实例详解、php 多用户读写文件冲突的解决办法实例详解、PHP 文件写入和读取操作实例详解【必看篇】、PHP与ThinkPHP读写文件的实用技巧。
本文目录一览:- PHP读写文件高并发处理操作实例详解(php 并发读写文件)
- php 中的信号处理操作实例详解
- php 多用户读写文件冲突的解决办法实例详解
- PHP 文件写入和读取操作实例详解【必看篇】
- PHP与ThinkPHP读写文件
PHP读写文件高并发处理操作实例详解(php 并发读写文件)
本文实例讲述了PHP读写文件高并发处理操作。分享给大家供大家参考,具体如下:
背景:
最近公司游戏开发需要知道游戏加载的流失率。因为,我们做的是网页游戏。玩过网页游戏的人都知道,进入游戏前要加载一些资源。最后才能到达创建角色的游戏界面。我们有一个需求就是要统计在加载过程中还未到达角色创建界面而流失的用户数量。
我们在加载开始就进行统计人数,加载完成之后再记录人数。这样,通过用加载前的人数减去成功加载后的人数。就知道了加载的流失率。就可以知道游戏是否还要继续优化加载过程,降低用户加载游戏率。
由于,我们的量都是从*主流的合作媒体进行导量过来。所以,并发非常高,据粗略计算应该能达到每秒1000左右的并发数量。
加载前的人数本来想放到游戏内部的缓存平台。但是,游戏后端的同事担心并发太高,导致资源无故浪费。因为,内存的释放并不是实时响应的。所以,将统计的人数放到在另外一台服务器:统计服务器。
我刚开始采用的方案如下:
通过PHP的file_get_contents()
与file_put_contents()
进行读取与写入。第一次读写就向文件写入1,第二次加载就在原来的基础上加1.以此类推.这种顺序的思想完全不存在任何问题。问题就出在,我们的服务器不可能是顺序形式的。
准确的说,并发的访问不是顺序的。当A玩家加载游戏读取到文件里面的数字100(假如这时是100),B玩家读取到的也是100,这时,处理A玩家的线程就是在100的基础上加1,得到101,就会向文件写入101。
处理B玩家的线程也得到相同的结果,将101写入文件。这时,问题就出现了?B玩家是在A玩家之后加载游戏的,理应得到102的计算结果。
这就是并发导致的问题。这个时候,我想到了采用fopen()
打开文件,并用flock()
加一个写入锁。大家一定会认为,这种方式有了锁定,那么就不会造成问题了。其实,也是错的。
因为,我们的问题不是出在写入上面。而是读取的时候造成数据的不同步。OK。到这里,我实在百度谷歌都搞不定了。
当希望寄托在PHP函数本身而梦碎的时候,我只能另寻它法。脱离它。于是,我想到了*语言的Map映射的机制。类似于我们的PHP数组,每加载一次就我往数组添加一个元素。这样,到最后我只需要count()
一下数组就知道了有多少玩家加载了游戏。
但是,用数组的话,也存在一个问题。就是PHP的变量还是常量,在脚本执行完毕之后都会自己清掉。于是,我想到了文件保存的方式。
最终的可行方案思路如下:
用fopen打开一个文件,以只写的方式。然后写锁定。玩家每加载一次我就向文件里面写入一个数字1,最后得到的文件内容通过file_get_contents()
一次性读取出来,再用strlen()
计算一下长度即知道了有多少玩家加载了游戏。
听闻flock()
函数会锁定会造成系统资源在很多时间升高。所以,我采用大家所使用的方式,用微秒超时的技术解决这个问题。如果,走出这个时间我就*掉它。具体的代码如下:
以下是我调用以上方法的文件:
这是血的教训,所以,我不得不将它记录下来。以备以后让他人借鉴。
本文是作者寒冰一年前在4399游戏工作室负责做数据分析的时候写的代码。希望对大家有所帮助。
PHP数据库操作之高并发实例
高并发下PHP写文件日志丢失
更多关于PHP相关内容感兴趣的读者可查看本站专题:《》、《》、《@L_301_1@》、《》、《》、《》及《》
希望本文所述对大家PHP程序设计有所帮助。
php 中的信号处理操作实例详解
本文实例讲述了php 中的信号处理操作。分享给大家供大家参考,具体如下:
首先我们需要了解几个函数
pcntl_signal 安装信号处理器,也就是当指定信号发生时,调用函数。
pcntl_alarm 指定秒数后向进程发送SIGALRM信号。
posix_getpid 返回当前进程id
posix_kill 给指定进程发送信号
一些常用的信号说明
SIGHUP
本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时,
通知同一session内的各个作业, 这时它们与控制终端不再关联。
SIGINT
程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出。
SIGQUIT
和SIGINT类似,但由QUIT字符(通常是Ctrl-/)来控制;进程在因收到SIGQUIT退出时会产生core文件,
在这个意义上类似于一个程序错误信号。
SIGKILL
用来立即结束程序的运行。本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
SIGTERM
程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,
shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。
SIGUSR1
留给用户使用
SIGUSR2
留给用户使用
SIGALRM
时钟定时信号, 计算的是实际的时间或时钟时间。alarm函数使用该信号。
SIGCHLD
子进程结束时, 父进程会收到这个信号。
例1:
<?php declare(ticks = 1); //信号处理函数 function sig_func() { echo "child exit \r\n"; } //设置信号处理器 pcntl_signal(SIGCHLD, ''sig_func''); $pid = pcntl_fork(); if($pid == -1) { die(''fork error''); } else if ($pid) { pcntl_wait($status); } else { echo "child... \r\n"; exit; }
当子进程退出时,会向父进程发送SIGCHLD信号,我们通过设置信号处理器,成功的处理信号。
例2:
<?php declare(ticks = 1); //信号处理函数 function sig_func($signo) { switch($signo) { case SIGCHLD: { echo "child SIGCHLD \r\n"; break; } case SIGTERM: { echo "child SIGTERM \r\n"; break; } default: //处理所有其他信号 break; } } //设置信号处理器 pcntl_signal(SIGCHLD, ''sig_func''); //设置信号处理器 pcntl_signal(SIGTERM, ''sig_func''); $pid = pcntl_fork(); if($pid == -1) { die(''fork error''); } else if ($pid) { pcntl_wait($status); } else { sleep(3); echo "child \r\n"; sleep(3); posix_kill(getmypid(), SIGTERM); exit; }
父进程等待子进程的退出,子进程等待3秒后输出child,再等待3秒后向自身发送结束程序信号。
例3:
<?php declare(ticks = 1); //信号处理函数 function sig_func($signo) { switch($signo) { case SIGCHLD: { echo "child SIGCHLD \r\n"; break; } /*这里要把处理SIGTERM信号的代码注释掉 case SIGTERM: { echo "child SIGTERM \r\n"; break; }*/ default: //处理所有其他信号 break; } } //设置信号处理器 pcntl_signal(SIGCHLD, ''sig_func''); //设置信号处理器,也注释掉 //不然当父进程发向子进程发送SIGTERM信号时,子进程不会退出,还会继续执行 //我们的信号处理函数把SIGTERM给忽略了 //pcntl_signal(SIGTERM, ''sig_func''); $pid = pcntl_fork(); if($pid == -1) { die(''fork error''); } else if ($pid) { sleep(30); posix_kill($pid, SIGTERM); } else { $cnt = 0; for(;;) { sleep(3); echo $cnt, ''-''; ++$cnt; } exit; }
父进程在等待30秒后,向子进程发送SIGTERM结束程序信号。如果我们设置了SIGTERM信号的处理器,并且在自定义信号处理器中并没有杀死该进程,则该子进程会一直运行下去。
pcntl_signal()函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是pcntl_signal_dispatch()函数。
例4:
<?php //使用ticks需要PHP 4.3.0以上版本 //declare(ticks = 1); function sig_func() { echo "SIGALRM \r\n"; } //设置信号处理器 pcntl_signal(SIGALRM, ''sig_func''); pcntl_alarm(3);
通过函数pcntl_alarm()3秒后给进程发送SIGALRM信号,但信号处理函数并未调用。
原因是我们注释了declare(ticks = 1);这段代码,而又没有调用pcntl_signal_dispatch()函数。
declare(ticks = 1);表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。但是这种处理方式效率很低,建议在代码循环中通过pcntl_signal_dispatch()来处理信号。
<?php //使用ticks需要PHP 4.3.0以上版本 //declare(ticks = 1); function sig_func() { echo "SIGALRM \r\n"; } //设置信号处理器 pcntl_signal(SIGALRM, ''sig_func''); pcntl_alarm(3); //因为3秒后pcntl_alarm函数才会给进程发送SIGALRM信号 //所以我们通过sleep函数等待3秒后,调用pcntl_signal_dispatch()来处理信号 sleep(3); pcntl_signal_dispatch();
pcntl_signal_dispatch()这个函数是PHP5.3以上才支持的,如果你的PHP版本大于5.3,建议使用这个方法调用信号处理器。
5.3以下的版本需要在注册信号之前加上:declare(ticks = 1);
更多关于PHP相关内容感兴趣的读者可查看本站专题:《PHP进程与线程操作技巧总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》、《PHP数组(Array)操作技巧大全》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》
希望本文所述对大家PHP程序设计有所帮助。
- python多线程下信号处理程序示例
- PHP信号处理机制的操作代码讲解
- 对Python信号处理模块signal详解
- Golang信号处理及如何实现进程的优雅退出详解
- Perl信号处理学习简单小结
- 详细分析C++ 信号处理
php 多用户读写文件冲突的解决办法实例详解
解决多调用同时写一个文件时我们会使用flock来解决些问题,这样同一时间只有一个用户可以写文件,同时其它用户进行等待队列了,下面我来介绍一下flock解决多用户读写文件冲空问题
一般的方案会是:
代码如下:
$fp = fopen("/tmp/lock.txt", "w+"); if (flock($fp, LOCK_EX)) { fwrite($fp, "Write something heren"); flock($fp, LOCK_UN); } else { echo "Couldn't lock the file !"; } fclose($fp);
但在PHP中,flock似乎工作的不是那么好!在多并发情况下,似乎是经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的cpu占用很高,甚至有时候会让服务器彻底死掉。好像在很多linux/unix系统中,都会有这样的情况发生。
所以使用flock之前,一定要慎重考虑。
那么就没有解决方案了吗?其实也不是这样的。如果flock()我们使用得当,完全可能解决死锁的问题。当然如果不考虑使用flock()函数,也同样会有很好的解决方案来解决我们的问题。
经过我个人的搜集和总结,大致归纳了解决方案有如下几种。
方案一:对文件进行加锁时,设置一个超时时间.
大致实现如下:
代码如下:
if($fp = fopen($fileName, 'a')) { $startTime = microtime(); do { $canWrite = flock($fp, LOCK_EX); if(!$canWrite) usleep(round(rand(0, 100)*1000)); } while ((!$canWrite)&& ((microtime()-$startTime) < 1000)); if ($canWrite) { fwrite($fp, $dataToSave); } fclose($fp); }
超时设置为1ms,如果这里时间内没有获得锁,就反复获得,直接获得到对文件操作权为止,当然。如果超时限制已到,就必需马上退出,让出锁让其它进程来进行操作。
方案二:不使用flock函数,借用临时文件来解决读写冲突的问题。
大致原理如下:
1。将需要更新的文件考虑一份到我们的临时文件目录,将文件最后修改时间保存到一个变量,并为这个临时文件取一个随机的,不容易重复的文件名。
2。当对这个临时文件进行更新后,再检测原文件的最后更新时间和先前所保存的时间是否一致。
3。如果最后一次修改时间一致,就将所修改的临时文件重命名到原文件,为了确保文件状态同步更新,所以需要清除一下文件状态。
4。但是,如果最后一次修改时间和先前所保存的一致,这说明在这期间,原文件已经被修改过,这时,需要把临时文件删除,然后返回false,说明文件这时有其它进程在进行操作。
大致实现代码如下:
代码如下:
$dir_fileopen = "tmp"; function randomid() { return time().substr(md5(microtime()), 0, rand(5, 12)); } function cfopen($filename, $mode) { global $dir_fileopen; clearstatcache(); do { $id = md5(randomid(rand(), TRUE)); $tempfilename = $dir_fileopen."/".$id.md5($filename); } while(file_exists($tempfilename)); if (file_exists($filename)) { $newfile = false; copy($filename, $tempfilename); }else{ $newfile = true; } $fp = fopen($tempfilename, $mode); return $fp ? array($fp, $filename, $id, @filemtime($filename)) : false; } function cfwrite($fp,$string) { return fwrite($fp[0], $string); } function cfclose($fp, $debug = "off") { global $dir_fileopen; $success = fclose($fp[0]); clearstatcache(); $tempfilename = $dir_fileopen."/".$fp[2].md5($fp[1]); if ((@filemtime($fp[1]) == $fp[3]) || ($fp[4]==true && !file_exists($fp[1])) || $fp[5]==true) { rename($tempfilename, $fp[1]); }else{ unlink($tempfilename); //说明有其它进程 在操作目标文件,当前进程被拒绝 $success = false; } return $success; } $fp = cfopen('lock.txt','a+'); cfwrite($fp,"welcome to beijing.n"); fclose($fp,'on');
对于上面的代码所使用的函数,需要说明一下:
1.rename();重命名一个文件或一个目录,该函数其实更像linux里的mv。更新文件或者目录的路径或名字很方便。
但当我在window测试上面代码时,如果新文件名已经存在,会给出一个notice,说当前文件已经存在。但在linux下工作的很好。
2.clearstatcache();清除文件的状态.php将缓存所有文件属性信息,以提供更高的性能,但有时,多进程在对文件进行删除或者更新操作时,php没来得及更新缓存里的文件属性,容易导致访问到最后更新时间不是真实的数据。所以这里需要使用该函数对已保存的缓存进行清除。
方案三:对操作的文件进行随机读写,以降低并发的可能性。
在对用户访问日志进行记录时,这种方案似乎被采用的比较多。
先前需要定义一个随机空间,空间越大,并发的的可能性就越小,这里假设随机读写空间为[1-500],那么我们的日志文件的分布就为log1~到log500不等。每一次用户访问,都将数据随机写到log1~log500之间的任一文件。
在同一时刻,有2个进程进行记录日志,A进程可能是更新的log32文件,而B进程呢?则此时更新的可能就为log399.要知道,如果要让B进程也操作log32,概率基本上为1/500,差不多约等于零。
在需要对访问日志进行分析时,这里我们只需要先将这些日志合并,再进行分析即可。
使用这种方案来记录日志的一个好处时,进程操作排队的可能性比较小,可以使进程很迅速的完成每一次操作。
方案四:将所有要操作的进程放入一个队列中。然后专门放一个服务完成文件操作。
队列中的每一个排除的进程相当于第一个具体的操作,所以第一次我们的服务只需要从队列中取得相当于具体操作事项就可以了,如果这里还有大量的文件操作进程,没关系,排到我们的队列后面即可,只要愿意排,队列的多长都没关系。
对于以前几种方案,各有各的好处!大致可能归纳为两类:
1、需要排队(影响慢)比如方案一、二、四
2、不需要排队。(影响快)方案三
在设计缓存系统时,一般我们不会采用方案三。因为方案三的分析程序和写入程序是不同步的,在写的时间,完全不考虑到时候分析的难度,只管写的行了。试想一下,如我们在更新一个缓存时,如果也采用随机文件读写法,那么在读缓存时似乎会增加很多流程。但采取方案一、二就完全不一样,虽然写的时间需要等待(当获取锁不成功时,会反复获取),但读文件是很方便的。添加缓存的目的就是要减少数据读取瓶颈,从而提高系统性能。
立即学习“PHP免费学习笔记(深入)”;
以上就是php 多用户读写文件冲突的解决办法实例详解的详细内容,更多请关注php中文网其它相关文章!
PHP 文件写入和读取操作实例详解【必看篇】
本文实例讲述了PHP 文件写入和读取操作。分享给大家供大家参考,具体如下:
文章提纲:
一.实现文件读取和写入的基本思路
二.使用fopen方法打开文件
三.文件读取和文件写入操作
四.使用fclose方法关闭文件
五.文件指针的移动
六.Windows和UNIX下的回车和换行
一.实现文件读取和写入的基本思路:
1.通过fopen方法打开文件:$fp =fopen(/*参数,参数*/),fp为Resource类型
2.进行文件读取或者文件写入操作(这里使用的函数以1中返回的$fp作为参数)
3. 调用fclose($fp)关闭关闭文件
二:使用fopen方法打开文件
fopen(文件路径[string],打开模式[string])
<1>fopen的第一个参数为文件路径
写文件路径的方式:1绝对路径,2相对路径
1绝对路径:
在windows下工作的小伙伴们应该很熟悉,windows下的路径分隔符是“\”而不是“/”,但我们在写入路径时不能以钦定的“\”为分隔符
那如果我们以“\”分隔符写入路径会怎样呢?
<?php $fp = fopen("C:\wamp64\www\text.txt",''w''); ?>
运行后报错,提示路径参数无效
所以我们要把分隔符“\”换成“/”:
<?php $fp = fopen("C:/wamp64/www/text.txt",''w''); ?>
运行时无报错,说明参数是有效的。
【注意】fopen函数不能理解“\”分隔符,如果你想要使用“\”,那么要使用转义,如写成:"C:\\wamp64\\www\\text.txt"这种写法也是可以的,函数也能理解,不会报错。但即使这样,也不推荐使用“\”,因为在OS(mac)下只能识别“/”不能识别“\”
本小节的结论:推荐坚持使用“/”作为分隔符
2.相对路径:
上一小节介绍的是绝对路径的写法,但这样却带来了另外一个问题:服务器的目录结构可能会有较大的改变,这时原来写的绝对路径就要全部重写了,比如在我的电脑上的目标文件路径是C:/wamp64/www/text.txt,如果我把www文件夹改名为penghuwan呢?原来写入的路径参数就失效了。所以我们引入了相对路径的写法:
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''w''); ?>
• $_SERVER是PHP的超级全局变量(在代码任何地方都可访问,类型是数组),通过$_SERVER[''DOCUMENT_ROOT'']可取到服务器的默认根目录
服务器的默认根目录可通过php.ini修改(这个可自行百度)
• $_SERVER[''DOCUMENT_ROOT'']在这里等同于C:/wamp64/www
本小节的结论:推荐使用相对路径
<2>fopen的第二个参数为打开模式
设置打开模式后,我们就相当于为接下来的读写操作设置了权限:
最基本的几个模式:
“r”:只能读取文件,不能写入文件(写入操作被忽略)
“w”:只能写入文件,不能读取文件(读取操作被忽略)
“a”:只追加文件,与“w”类似,区别是“w”删除原有的内容,“a”不删除原有内容,只追加内容
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''w''); fwrite($fp,''在写模式下写入''); fclose($fp); ?>
在设置了写操作的权限后,就能正常地写入文件了
运行后打开C:/wamp64/www/text.txt:
这次我们把权限设置为只读,并尝试写入文本:''在只读模式下写入''
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); fwrite($fp,''在读模式下写入''); fclose($fp); ?>
运行后打开C:/wamp64/www/text.txt,发现文件内容并没有改变,说明由于没有设置相应的权限,操作被忽略了
关于打开模式的网络资料,我想大家最可能找到的是这张表:(图来自W3C)
很全面,但我觉得这张表对新手有些不太友好,让人看后不知多云。 r是只读,w是只写(原来有的内容全删除),a是追加(不删除原有内容),这都好理解。
但r+,w+,和a+的区别和联系讲的实在太模糊了呀。 这里我就想详细地讲一下r+,w+,和a+三者的区别和联系:
首先r+,w+,和a+都是可读可写的,读取时的方式是一样的,关键在于写入方式的不同:
r+: 从文件[头部][覆盖]原有内容 ([不删除]原有内容);
a+:从文件[尾部][追加]内容 ([不删除]原有内容);
w+:[完全删除]原有内容,然后[再添加]新的内容
下面我依次演示上述的结论,首先我们没有写入的时候文本是”I am initialized value”(意为我是初始值)
• 采用r+模式写入文本“r+ mode”
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r+''); fwrite($fp,''r+ mode''); fclose($fp); ?>
运行后再打开文本,发现“I am in”被“r+ mode”覆盖了:
• 采用a+模式写入文本“a+ mode”
基于”I am initialized value”的初始文本我们运行以下代码:
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''a+''); fwrite($fp,''a+ mode''); fclose($fp); ?>
I am initialized value没有被删除和覆盖,而是在后面追加了a+ mode的这一段新文本运行多次后:
•采用w+模式写入文本“w+ mode”
基于”I am initialized value”的初始文本我们运行以下代码:
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''w+''); fwrite($fp,''w+ mode''); fclose($fp); ?>
运行后,我们发现”I am initialized value”已经被删除了,然后才加上了“w+ mode”这段新文本
【注意】r+,a+,w+还有一个区别是a+,w+在文件不存在时则创建文件,r+文件不存在时报错
【吐槽】:关于r+和w+,a+的区别,我找了网络上,包括W3C和各种博客文章以及那本“PHP圣经”上的各种资料,发现都是一笔带过去的,这也是我写这篇文章的原因
三.文件读取和文件写入操作
先说说几个比较重要的函数:
• file_exists():判断文件是否存在,返回布尔值
• filesize():判断一个文件大小,返回文件的字节数,为整型数字
• unlink():删除一个文件
写入文件
fwrite(资源文件对象[string],写入方式[string]),资源文件对象即为fopen方法返回的参数,为Resource类型,写入方式可以是w(或者w+,a+,r+) 已经有上面的例子,这里就不放demo了
读取文件
这是我们要读取的文件内容:
读取文件的方式有以下几种:
1.一次读取一个字节的数据 fgetc()
2.一次读取指定的字节数的数据 fread()
3.一次读取一行数据 fgets()/fgetcsv()
4.一次读完全部数据 fpassthru()/ file()
1. 一次读取一个字节 —— 通过fgetc()获取单个字节
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r'');//打开文件 if(file_exists("$DOCUMENT_ROOT/text.txt")){//当文件存在时,才读取内容 while(!feof($fp)){//判断文件指针是否到达末尾 $c = fgetc($fp);//每执行一次fgetc(),文件指针就向后移动一位 echo $c;//输出获取到的字节 } } fclose($fp);//关闭文件 ?>
运行:
【注意】:无论是按文本格式输入输出
还是按二进制格式输出,fgetc()每次获取的是一个字节而不是一个字符!上面的例子中我们是逐个输出,现在让我们只做一次输出,看看结果怎样:
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); echo fgetc($fp);//只做一次输出 close($fp); ?>
运行结果如下,我们得到的不是汉字“我”,而是一个乱码,其实这个乱码就是一个字节
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); echo fgetc($fp);//连续做三次输出 echo fgetc($fp); echo fgetc($fp); fclose($fp); ?>
2.一次读取多个字节 ——通过fread()方法:
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); echo fread($fp, 3);//一次输出三个字节即一个汉字字符(UTF-8) fclose($fp); ?>
运行结果:
改成:
echo fread($fp, 6);
运行结果如下,输出了6个字节也即两个汉字字符(UTF-8)
3.一次读取一行——通过fgets()获取一行内容
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT''] $fp = fopen("$DOCUMENT_ROOT/text.txt",''r'');//打开文件 if(file_exists("$DOCUMENT_ROOT/text.txt")){//当文件存在时,才读取内容 while(!feof($fp)){//判断文件指针是否到达末尾 $line = fgets($fp);//返回一行文本,并将文件指针移动到下一行头部 echo $line."<br/>";//输出获取到的一行文本 } } fclose($fp);//关闭文件 ?>
fgets()其实还有第二个参数,这个参数规定了每一行能读取的最大字节数(注意是字节数不是字符数):
【注意】在UTF-8编码下汉字3字节,字母1字节下面我修改上面的一行,代码,使获取的每一行最大字符数为3(也即字节数为9)
$line = fgets($fp,10);
Demo:
【注意】:这里我fgets()里第二个参数为10,为什么是10呢?因为
1.这里的长度是按字节数算的
2.一个汉字占3个字节。fgets($fp,10)代表一次最多读取10 - 1 = 9字节
4.一次读完全部文件 ——fpassthru() or file()? fpassthru()将读取文件并直接输出(无处理过程)
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); fpassthru($fp); fclose($fp); ?>
运行结果:
【注意】这里需要注意一点的是,我们并没有从fpassthru($fp)获取到返回值然后echo到页面上去,也就是说这个方法是会强制输出获取的内容的,而并不是像之前例子的方法那样返回文本,允许我们保存到变量中才将其输出
将读取到的全部内容保存到一个数组中,每个数组元素为一行的内容——fille()
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $file_array = file("$DOCUMENT_ROOT/text.txt");//取到文件数组 foreach ($file_array as $value) {//输出数组元素 echo $value."<br/>"; } ?>
【注意】:这里我们并不需要写fopen和fclose哦!也就是说file()方法已经帮我们做了这一步了
四.使用fclose方法关闭文件
fclose()将返回一个布尔值,成功关闭为true,关闭失败为false(失败的情况很少出现,可不考虑) 是否打开文件后一定要关闭?
1即使不手写fclose,在PHP脚本执行结束后,也会自动关闭文件的
2但在一个长时间执行的脚本中,如果不写关闭文件的fclose(),在文件加锁的情况下会造成操作的阻塞,所以,写fclose是个好习惯
五.文件指针的移动
我们上面调用的读取文件的函数,其实都是基于文件指针去打印的,每读取一段字节内容,文件指针就向后移动一段字节长度,直到被读取的文件最大字节长度为止
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; function print_file_pointer($fp){//定义一个打印文件指针位置的函数 echo " <br/>//此时文件指针的位置:"; echo ftell($fp)."<br/>"; } $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); echo fgetc($fp);//通过fgetc连续输出三个字节 echo fgetc($fp); echo fgetc($fp); print_file_pointer($fp);//打印此刻文件指针的位置 echo fread($fp,6);//通过fread一次输出6字节 print_file_pointer($fp);//打印此刻文件指针的位置 echo fgets($fp); //通过fgets输出一整行 print_file_pointer($fp);//打印此刻文件指针的位置 fpassthru($fp); //一次性输出全部内容 print_file_pointer($fp);//打印此刻文件指针的位置 fseek($fp, 33);//使文件指针移动到33字节位置 print_file_pointer($fp);//打印此刻文件指针的位置 rewind($fp);//使文件指针移动到0字节位置(初始位置) print_file_pointer($fp);//打印此刻文件指针的位置 $fclose($fp); ?>
Demo:
所以我们需要正确理解fgets(),fpassthru()这些函数的作用:
fgets():从当前文件指针的位置到本行结束的数据,而不是一定输出一整行
fpassthru():从当前文件指针的位置到全部内容结束的数据,而不是一定输出所有的数据
但在这里你可能会有疑问:为什么输出“湖湾”后的指针位置会是17而不是15呢?按理说输出“我叫彭湖湾”这5个汉字一共占3*5 = 15个字节,多出来的17 - 15 =2字节是什么呢?
多出来的两个字节是windows下的回车换行符\n\r
\n是换行,占一字节,\r是回车,占一字节,在六中我将会介绍
六.Windows和UNIX下的回车和换行
<?php $DOCUMENT_ROOT = $_SERVER[''DOCUMENT_ROOT'']; $fp = fopen("$DOCUMENT_ROOT/text.txt",''r''); while(!feof($fp)){ echo fgets($fp); echo ftell($fp); } fclose($fp); ?>
我们在windows下敲下回车键的时候,相当于键入了\n\r,所以“我叫彭湖湾”的15字节+“\n\r”的2字节 = 17字节
在mac下不一样的是:敲下回车键的时候,相当于只键入了\n,所以“我叫彭湖湾”的15字节+“\n”的1字节 = 16字节
更多关于PHP相关内容感兴趣的读者可查看本站专题:《php文件操作总结》、《PHP目录操作技巧汇总》、《PHP常用遍历算法与技巧总结》、《PHP数据结构与算法教程》、《php程序设计算法总结》及《PHP网络编程技巧总结》
希望本文所述对大家PHP程序设计有所帮助。
- thinkphp5.1 框架导入/导出excel文件操作示例
- PHP实现文件上传操作和封装
- PHP检查文件是否存在,不存在自动创建及读取文件内容操作示例
- php利用ZipArchive类操作文件的实例
- php遍历目录下文件并按修改时间排序操作示例
- PHP 实现文件压缩解压操作的方法
- PHP微信网页授权的配置文件操作分析
- PHP文件操作简单介绍及函数汇总
PHP与ThinkPHP读写文件
-
使用php将数据写入到指定的文件
$str="<?php return".var_export($phiz,true)."?>";
file_put_contents(''./Data/phiz.php'');
-
使用php读取指定的文件 $phiz=include ''./Data/phiz.php''; 或者:$phiz=file_get_contents($phiz); dump($phiz);
-
使用tp的方法将数据写入到指定的文件
F(''phiz'',$phiz,''./Data/'');
4. 使用tp的方法将指定文件的读取出来
$phiz=F(''phiz'','''',''./Data/'');
关于PHP读写文件高并发处理操作实例详解和php 并发读写文件的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于php 中的信号处理操作实例详解、php 多用户读写文件冲突的解决办法实例详解、PHP 文件写入和读取操作实例详解【必看篇】、PHP与ThinkPHP读写文件的相关信息,请在本站寻找。
本文标签: