首页 > 开发 > PHP > 正文

PHP中fwrite与file_put_contents的区别

2024-05-04 22:01:21
字体:大 中 小
来源:转载
供稿:网友

相同点:file_put_contents() 函数把一个字符串写入文件中,与依次调用 fopen(),fwrite() 以及 fclose() 功能一样。

不同点:在file_put_contents()函数中使用 FILE_APPEND 可避免删除文件中已有的内容,即实现多次写入同一个文件时的追加功能。

例如: 

echo file_put_contents("test.txt","Hello World. Testing!",FILE_APPEND);

file_put_contents是以追加的形式将字符串写入到test.txt中,fwrtie则是会清除之前的记录,只保留当前写入的内容

  1. $file = fopen("test.txt","w"); 
  2. echo fwrite($file,"Hello World. Testing!"); 
  3. fclose($file); 

file_put_contents代替fwrite优点多多

如下为file_put_contents的实例代码:

  1. <?php 
  2. $filename = 'file.txt'; 
  3. $word = "你好!/r/nwebkaka";  //双引号会换行 单引号不换行 
  4. file_put_contents($filename, $word); 
  5. ?> 

同样的功能使用fwrite的实例代码:

  1. <?php 
  2. $filename = 'file.txt'; 
  3. $word = "你好!/r/nwebkaka";  //双引号会换行  单引号不换行 
  4. $fh = fopen($filename, "w"); //w从开头写入 a追加写入 
  5. echo fwrite($fh, $word); 
  6. fclose($fh); 
  7. ?> 

从以上两个例子看出,其实file_put_contents是fopen、fwrite、fclose三合一的简化写法,这对程序代码的优化是有好处的,一方面在代码量上有所减少,另一方面不会出现fclose漏写的不严密代码,在调试、维护上方便很多。

上述例子里,file_put_contents是从头写入,如果要追加写入,怎么办呢?

在file_put_contents的语法里,有个参数FILE_APPEND,这是追加写入的声明。实例代码如下:

  1. <?php 
  2. echo file_put_contents('file.txt', "This is another something.", FILE_APPEND); 
  3. ?> 

FILE_APPEND就是追加写入的声明。在追加写入时,为了避免其他人同时操作,往往需要锁定文件,这时需要加多一个LOCK_EX的声明,写法如下:

  1. <?php 
  2. echo file_put_contents('file.txt', "This is another something.", FILE_APPEND|LOCK_EX); 
  3. ?> 

注意,以上代码中echo输出到显示器里的是写入文件字符串的长度。

常见问题:

Warning: fopen(file.txt) [function.fopen]: failed to open stream: Permission denied

当写入文件时,有时会遇到上述问题,这是因为文件没有写权限的原因。为了避免这个错误的出现,在写入文件时需要判断下文件是否可写,这需要用到is_writable()这个函数。实例代码如下:

  1. <?php 
  2. $filename = 'file.txt'; 
  3. if (is_writable($filename)) { 
  4. echo file_put_contents($filename, "This is another something.", FILE_APPEND); 
  5. } else { 
  6.     echo "文件 $filename 不可写"; 
  7. } 
  8. ?> 

fwrite简单的把数据写到handler里面。

file_put_contents可能需要处理contenxt,数据类型为mixed,需要更多处理,虽然看file_put_contents的函数说明:和依次调用 fopen(),fwrite() 以及 fclose() 功能一样。

但是肯定有细微差别的,尤其是在重复写入大量数据的时候,file_put_contents无疑会重复的fopen,fclose .而 fwrite则可以只一次fopen,fwrite即可。

写个简单程序测试一下,一个250M文件

  1. <!-- 
  2. <?php 
  3. $len = 1024*1024*25; 
  4. $data = str_repeat(“-”,$len); 
  5. $start = microtime(true); 
  6. $fp = fopen(“/tmp/b”,”w”); 
  7. fwrite($fp,$data,$len); 
  8. fclose($fp); 
  9. $end = microtime(true); 
  10. echo “elipsed time:”.($end-$start).”/n”; 
  11. $start = microtime(true); 
  12. file_put_contents(“/tmp/a”,$data); 
  13. $end = microtime(true); 
  14. echo “elipsed time:”.($end-$start).”/n”; 
  15. silver@silver-desktop:~/php$ php fwrite_VS_file_put_contents.php 
  16. elipsed time:6.0958020687103 
  17. elipsed time:9.6280250549316 
  18. silver@silver-desktop:~/php$ php fwrite_VS_file_put_contents.php 
  19. elipsed time:6.247565984726 
  20. elipsed time:9.0449070930481 
  21. … 
  22. --> 

结论:多次执行结果类试,说明fopen,fwrite,fclose方式比直接file_put_contents要快一点!

那么为什么呢? 查看源代码

我用的ubuntu12.04

直接sudo apt-get source php5

解压:silver@silver-desktop:~/php/php5-5.3.3

查找函数fwrite 函数:silver@silver-desktop:~/php/php5-5.3.3$ grep -rn “PHP_FUNCTION(fwrite)” .

./ext/standard/file.c:1233:PHPAPI PHP_FUNCTION(fwrite)

./ext/standard/file.h:43:PHPAPI PHP_FUNCTION(fwrite);

fwrite

找到对应源码,该函数非常简单:

  1. <!-- 
  2. PHP_FUNCTION(fwrite) 
  3. { 
  4. zval *arg1; 
  5. char *arg2; 
  6. int arg2len; 
  7. int ret; 
  8. int num_bytes; 
  9. long arg3 = 0; 
  10. char *buffer = NULL; 
  11. php_stream *stream; 
  12. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “rs|l”, &arg1, &arg2, &arg2len, &arg3) == FAILURE) { 
  13. RETURN_FALSE; 
  14. } 
  15. if (ZEND_NUM_ARGS() == 2) { 
  16. num_bytes = arg2len; 
  17. } else { 
  18. num_bytes = MAX(0, MIN((int)arg3, arg2len)); 
  19. } 
  20. if (!num_bytes) { 
  21. RETURN_LONG(0); 
  22. } 
  23. PHP_STREAM_TO_ZVAL(stream, &arg1); 
  24. if (PG(magic_quotes_runtime)) { 
  25. buffer = estrndup(arg2, num_bytes); 
  26. php_stripslashes(buffer, &num_bytes TSRMLS_CC); 
  27. } 
  28. ret = php_stream_write(stream, buffer ? buffer : arg2, num_bytes); 
  29. if (buffer) { 
  30. efree(buffer); 
  31. } 
  32. RETURN_LONG(ret); 
  33. } 
  34. --> 

file_put_contents

该函数的处理操作就多多了

  1. <!-- 
  2. PHP_FUNCTION(file_put_contents) 
  3. { 
  4. php_stream *stream; 
  5. char *filename; 
  6. int filename_len; 
  7. zval *data; 
  8. int numbytes = 0; 
  9. long flags = 0; 
  10. zval *zcontext = NULL; 
  11. php_stream_context *context = NULL; 
  12. php_stream *srcstream = NULL; 
  13. char mode[3] = “wb”; 
  14. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “sz/|lr!”, &filename, &filename_len, &data, &flags, &zcontext) == FAILURE) { 
  15. return; 
  16. } 
  17. if (Z_TYPE_P(data) == IS_RESOURCE) { 
  18. php_stream_from_zval(srcstream, &data); 
  19. } 
  20. context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); 
  21. if (flags & PHP_FILE_APPEND) { 
  22. mode[0] = ‘a’; 
  23. } else if (flags & LOCK_EX) { 
  24. /* check to make sure we are dealing with a regular file */ 
  25. if (php_memnstr(filename, “://”, sizeof(“://”) – 1, filename + filename_len)) { 
  26. if (strncasecmp(filename, “file://”, sizeof(“file://”) – 1)) { 
  27. php_error_docref(NULL TSRMLS_CC, E_WARNING, “Exclusive locks may only be set for regular files”); 
  28. RETURN_FALSE; 
  29. } 
  30. } 
  31. mode[0] = ‘c’; 
  32. } 
  33. mode[2] = ‘/0′; 
  34. stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); 
  35. if (stream == NULL) { 
  36. RETURN_FALSE; 
  37. } 
  38. if (flags & LOCK_EX && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) { 
  39. php_stream_close(stream); 
  40. php_error_docref(NULL TSRMLS_CC, E_WARNING, “Exclusive locks are not supported for this stream”); 
  41. RETURN_FALSE; 
  42. } 
  43. if (mode[0] == ‘c’) { 
  44. php_stream_truncate_set_size(stream, 0); 
  45. } 
  46. switch (Z_TYPE_P(data)) { 
  47. case IS_RESOURCE: { 
  48. size_t len; 
  49. if (php_stream_copy_to_stream_ex(srcstream, stream, PHP_STREAM_COPY_ALL, &len) != SUCCESS) { 
  50. numbytes = -1; 
  51. } else { 
  52. numbytes = len; 
  53. } 
  54. break; 
  55. } 
  56. case IS_NULL: 
  57. case IS_LONG: 
  58. case IS_DOUBLE: 
  59. case IS_BOOL: 
  60. case IS_CONSTANT: 
  61. convert_to_string_ex(&data); 
  62. case IS_STRING: 
  63. if (Z_STRLEN_P(data)) { 
  64. numbytes = php_stream_write(stream, Z_STRVAL_P(data), Z_STRLEN_P(data)); 
  65. if (numbytes != Z_STRLEN_P(data)) { 
  66. php_error_docref(NULL TSRMLS_CC, E_WARNING, “Only %d of %d bytes written, possibly out of free disk space”, numbytes, Z_STRLEN_P(data)); 
  67. numbytes = -1; 
  68. } 
  69. } 
  70. break; 
  71. case IS_ARRAY: 
  72. if (zend_hash_num_elements(Z_ARRVAL_P(data))) { 
  73. int bytes_written; 
  74. zval **tmp; 
  75. HashPosition pos; 
  76. zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(data), &pos); 
  77. while (zend_hash_get_current_data_ex(Z_ARRVAL_P(data), (void **) &tmp, &pos) == SUCCESS) { 
  78. if (Z_TYPE_PP(tmp) != IS_STRING) { 
  79. SEPARATE_ZVAL(tmp); 
  80. convert_to_string(*tmp); 
  81. } 
  82. if (Z_STRLEN_PP(tmp)) { 
  83. numbytes += Z_STRLEN_PP(tmp); 
  84. bytes_written = php_stream_write(stream, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); 
  85. if (bytes_written < 0 || bytes_written != Z_STRLEN_PP(tmp)) { 
  86. if (bytes_written < 0) { 
  87. php_error_docref(NULL TSRMLS_CC, E_WARNING, “Failed to write %d bytes to %s”, Z_STRLEN_PP(tmp), filename); 
  88. } else { 
  89. php_error_docref(NULL TSRMLS_CC, E_WARNING, “Only %d of %d bytes written, possibly out of free disk space”, bytes_written, Z_STRLEN_PP(tmp)); 
  90. } 
  91. numbytes = -1; 
  92. break; 
  93. } 
  94. } 
  95. zend_hash_move_forward_ex(Z_ARRVAL_P(data), &pos); 
  96. } 
  97. } 
  98. break; 
  99. case IS_OBJECT: 
  100. if (Z_OBJ_HT_P(data) != NULL) { 
  101. zval out; 
  102. //看看对像怎么保存的:) 
  103. if (zend_std_cast_object_tostring(data, &out, IS_STRING TSRMLS_CC) == SUCCESS) { 
  104. numbytes = php_stream_write(stream, Z_STRVAL(out), Z_STRLEN(out)); 
  105. if (numbytes != Z_STRLEN(out)) { 
  106. php_error_docref(NULL TSRMLS_CC, E_WARNING, “Only %d of %d bytes written, possibly out of free disk space”, numbytes, Z_STRLEN(out)); 
  107. numbytes = -1; 
  108. } 
  109. zval_dtor(&out); 
  110. break; 
  111. } 
  112. } 
  113. default: 
  114. numbytes = -1; 
  115. break; 
  116. } 
  117. php_stream_close(stream); 
  118. if (numbytes < 0) { 
  119. RETURN_FALSE; 
  120. } //Vevb.com 
  121. RETURN_LONG(numbytes); 
  122. } 
  123. --> 

什么时候用fwrite,file_put_contents ?

1,函数原型已经说明了它们处理的数据类型不一样

2,简单的文件处理,追求速度用fwrite

3,书写简单用file_put_contents – (啥类型的数据都能处理,magic阿。但是要理解类型判断机制,否则保存的数据可能不是你想要的)


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表