欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

php接收二进制文件怎么替换里面的内容

程序员文章站 2024-01-27 09:52:16
...
php接收二进制文件如下:
header('Content-type: text/html; charset=utf-8');
error_reporting(0);
$filename = $_GET["filename"];
$filesize = $_GET["filesize"];

$xmlstr = $GLOBALS[HTTP_RAW_POST_DATA];//$_POST["data"];//
if(empty($xmlstr)) $xmlstr = file_get_contents('php://input');

$raw = $xmlstr;//得到post过来的二进制原始数据
$file = fopen("./upload/".$filename,"w");//打开文件准备写入
fwrite($file,$raw);//写入
fclose($file);//关闭
?>

接收的二进制文件中非标准字符(>0x7F)的字节被替换为三个字节,比如E2替换为EF 9F A2
现在我想在Php接收到文件之后还原回去,把EF 9F A2替换为E2,请问这个怎么实现?

非常感谢!


回复讨论(解决方案)

你收到的不是一个 XML 流吗?
是文本文件而非二进制文件呀
>0x7F 的不都是 utf-8 宽字符吗?

你最好贴出 $raw 的内容看看

fopen("./upload/".$filename,"w b");
试试

补充
utf-8的EF 9F A2 应该是unicode F7E2 而不是 E2
且E000-F8FF属于用户自定义字符(就是没有字符集以外,交给用户使用的区域)
举例就是某些通信接口用自定义字符来做边界识别

要留意这个

发送端,格式是这样设置的:
if (xmlhttp.overrideMimeType) {
xmlhttp.overrideMimeType('text/plain; charset=x-user-defined');
} else {
xmlhttp.setRequestHeader('Accept-Charset', 'x-user-defined');
}
xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");

发送文件和接收文件:

你传送的是二进制流
按 XML 的约定,应以 base64 编码进行传送的
否则当做文本传送就会有问题的

传送时是否可以声明为二进制数据,这个需要查资料

在网上找到这种方法:
XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
function byteValue(x) {
return x.charCodeAt(0) & 0xff;
}
var ords = Array.prototype.map.call(datastr, byteValue);
var ui8a = new window.Uint8Array(ords);
this.send(ui8a.buffer);
}
xmlhttp.sendAsBinary(content);

在PC上用IE浏览器就可以。
但是我需要用在android 浏览器上,不支持这个函数Uint8Array,所以不知道怎么用二进制流发送了。

比较发送文件和接收文件,还是有规律的:
大于C0的加前缀为EF 9F,如E2替换为EF 9F A2
小于C0的加前缀为EF 9E,如B2替换为EF 9E B2

这样的编码不知道是怎么出来的?所以想说对接收文件的十六进制做替换,只要结果正确就行了。

有没有什么方法可以替换二进制数的?
正则表达式能支持吗?

str_replace、strtr 都可以,正则也可以

$s = str_replace("\xEF\x9E\xB2", "\xB2", $s);

不过
大于C0的加前缀为EF 9F,如E2替换为EF 9F A2
的规则是什么?

他的数据转换规则是这样的:
文件头不变,文件头多长不清楚,图上到0050h都一样的
>=80的字节,高位加上F7,再转utf-8,例如E2就变成F7E2然后转utf-8成为EF 9F A2

至少他图中E2/FB/91/B2/81...都满足这个规则

他图示的是 sqlite 数据库文件,显然数据库中是什么样数据都可以存放的,字节值 0x00 - 0xff
若能证实确如 #10 所说,那么写个函数就搞定了
算法测试

$s =00 2D E2 1A 05 00 00 00 01 03 FB 00 00 00 00 0E   
03 FB 02 6B 01 91 02 36 00 B2 01 60 00 81 00 81
00 2F

str_replace、strtr 都可以,正则也可以

$s = str_replace("\xEF\x9E\xB2", "\xB2", $s);

不过
大于C0的加前缀为EF 9F,如E2替换为EF 9F A2
的规则是什么?

大于C0的加前缀为EF 9F,将EF 9F A2替换为A2+40,这个正则表达式怎么写呢?

用正则可这样写
假定数据已把存在变量 $s 中,则

$s = preg_replace_callback('/[\xef]../', 'foo', $s);function foo($r) {  $c = (ord($r[0]{1}) & 0x03)   

他图示的是 sqlite 数据库文件,显然数据库中是什么样数据都可以存放的,字节值 0x00 - 0xff
若能证实确如 #10 所说,那么写个函数就搞定了
算法测试

$s =00 2D E2 1A 05 00 00 00 01 03 FB 00 00 00 00 0E   
03 FB 02 6B 01 91 02 36 00 B2 01 60 00 81 00 81
00 2F

另外请教下,这样转换的$c是字符形式打印出来,如果以十六进制写回文件,要怎么处理?
我用
$raw[$j++]=$c;//raw是接收到的原始数据
不行,应该还要再转换一下吗?
我是想用$raw直接写文件的。

二进制数据中包含了很多不可打印的字符,为了直观的看到他们,一般将其以一个字节两个十六进制数的格式打印出来(比如你贴图中的软件)

将十六进制串转换成二进制数据,一般可用 pack('H*', 'ffffff')
php5.4 还提供了一个 hex2bin
echo hex2bin("6578616d706c65206865782064617461"); //example hex data

非常感谢,学习了