GBK、Shift-JIS、BIG5编码检测算法

字符串的编码检测需要使用自定义的映射表,使用系统自带的Codepage是不大可能有准确率的,系统Codepage会将它所有没定义的字符映射为空格。
GBK、Shift-JIS、BIG5的码表空间都是不连贯的,而它们的有效空间也不完全重合,这为检测编码类型提供了可能性。

检测算法:
1、建立字符映射表:将任一ANSI编码的所有字符全映射,从0x00到0xFFFF都有Unicode字符对应,但需要注意的是没有定义的字符统统映射到Unicode的0xFFFD(共三个映射表,既可用于检测也可用于转换)。
2、预设字符串的编码是Shift-JIS。
3、使用Shift-JIS的映射表从字符串第一个字符开始检测直至最后一个字符。如果遇到有字符映射到0xFFFD,设置预设编码是GBK,立刻停止步骤3,跳至步骤4。
4、如果预设编码是GBK,使用GBK的映射表从字符串第一个字符开始检测直至最后一个字符。如果遇到有字符映射到0xFFFD,设置预设编码是BIG5,立刻停止步 骤4,跳至步骤5。
5、如果预设编码是BIG5,使用BIG5的映射表从字符串第一个字符开始检测直至最后一个字符。如果遇到有字符映射到0xFFFD,设置预设编码是未知编码,立刻停止步 骤5,跳至步骤6。
6、返回预设编码。

这个算法的编码检测优先度是Shift-JIS>GBK>BIG5,也即如果顺利通过当前检测,则跳过后面所有检测。事实上,有大量字符串是能通过所有检测的。例如只有一个字符的字符串,假设这个字符是0x8140,在三个编码当中,都不会映射到Unicode的0xFFFD,因此能通过所有检测。但这没意义了。设定了优先度后是为了告诉用户最可能的一种编码。

为什么设定Shift-JIS>GBK>BIG5?
Shift-JIS的码表空间是0x00-0x7F、0xA1-0xDF、0x8140-0xFC4B
GBK的码表空间是0x00-0x7F、0x8140-0xFEFE
BIG5的码表空间是0x00-0x7F、0x8140-0xFEFE
但双字节段(0x8140以上)都不是全部已定义,Shift-JIS在0x8140以上的有效字符数是7724,GBK是21885,BIG5是19782。
GBK的覆盖面最大,有效空间基本覆盖了Shift-JIS,因此一个字符串如果能通过Shift-JIS检测,也差不多能通过GBK检测。如果将GBK的优先度设得比Shift-JIS高,那么大量真正是Shift-JIS编码的字符串就压根没机会返回给用户了。从反方向看,GBK中存在数量庞大的字符Shift-JIS没定义,Shift-JIS是高度覆盖不住GBK的,一个GBK文本从概率上没那么容易检测成Shift-JIS。也即:如果一个文本的真正编码是Shift-JIS,那么优先使用Shift-JIS检测自然不会有问题;如果它是GBK,那么优先使用Shift-JIS检测也不大会返回Shift-JIS。因此Shift-JIS应当优先于GBK。
Shift-JIS和BIG5的关系的考虑也类似。

从转换日系音乐cue、日文小说的宅用途出发,也应当将Shift-JIS设置为最高。

下面三张图是是Shift-JIS编码的小说“文学少女”と死にたがりの道化的转换结果。左边是当作本地编码的处理结果,可以无视。右边才是转换结果。
(程序和文本下载:http://code.google.com/p/unicue/downloads/detail?name=Ansi2Unicode_1.01.zip

使用Shift-JIS映射表转换,结果自然是正确的。

强行使用GBK映射表转换,没有出现标记0xFFFD(0xFFFD:�),也即能通过GBK检测

强行使用Big5转换,出现0xFFFD标记,也即通不过BIG5检测。BIG5跟Shift-JIS的有效空间重合度没那么高,区分相对容易一点

另外一个GBK文本强行使用Shift-JIS转换的结果。很容易就出现了0xFFFD标记

那么GBK和BIG5的优先度应该谁高呢?这里就见仁见智了。GBK的字符数比BIG5多,从概率上GBK相对容易覆盖住BIG5,BIG5相对不容易覆盖住GBK。倘若采用Shift-JIS和GBK之间的比较方法,应该是BIG5的优先度比GBK高。但从实际情况来看,真正BIG5编码的文本强行使用GBK映射表转换比较容易出现0xFFFD标记,真正GBK编码的文本强行使用BIG5映射表转换反而不容易出现0xFFFD标记。

GBK文本强行使用BIG5映射表转换的结果,不容易出现0xFFFD标记

BIG5文本使用BIG5映射表转换,正常的结果

BIG5文本强行使用GBK映射表转换,很容易出现0xFFFD标记

现实结果和表面现象完全相反。如果一个文本的真正编码是GBK,那么优先使用GBK检测自然不会有问题;如果它是BIG5,那么优先使用GBK检测也不大会返回GBK。因此余倾向于GBK的优先度高于BIG5。
阅读全文…

14,696 次浏览 | 1 条评论
2010年6月8日 | 归档于 程序

UTF-8到Unicode的转换以及UTF-8编码的检测

UTF-8到Unicode的转换和UTF-8编码的检测思路是一样的,转换是一个不完全的检测,而检测则是一遇到错误立刻退出循环

16位Unicode(无视32位Unicode了)到UTF-8的映射关系

U-0000 – U-007F: 0xxxxxxx
U-0080 – U-07FF: 110xxxxx 10xxxxxx
U-0800 – U-FFFF: 1110xxxx 10xxxxxx 10xxxxxx

转换函数

CString UTF8toUnicde(const char* utf8Str,UINT length)
{
	CString unicodeStr;
	unicodeStr=_T("");
	//转换
	WCHAR chr=0;
	for (UINT i=0;i<length;)
	{
		if ((0x80&utf8Str[i])==0) // ASCII
		{
			chr=utf8Str[i];
			i++;
		}
		else if((0xE0&utf8Str[i])==0xC0) // 110xxxxx 10xxxxxx
		{
			chr =(utf8Str[i+0]&0x3F)<<6;
			chr|=(utf8Str[i+1]&0x3F);
			i+=2;
		}
		else if((0xF0&utf8Str[i])==0xE0) // 1110xxxx 10xxxxxx 10xxxxxx
		{
			chr =(utf8Str[i+0]&0x1F)<<12;
			chr|=(utf8Str[i+1]&0x3F)<<6;
			chr|=(utf8Str[i+2]&0x3F);
			i+=3;
		}
		else // 不是UTF-8字符串
		{
			return unicodeStr;
		}
		unicodeStr.AppendChar(chr);
	}
	return unicodeStr;
}

判断函数,参数和上面是相同的

BOOL CheckUTF8(const char* AnsiStr,UINT length)
{
	UINT i=0;
	BOOL isUTF8=TRUE;
	while (i<length)
	{
		if ((0x80&AnsiStr[i])==0) // ASCII
		{
			i++;
			continue;
		}
		else if((0xE0&AnsiStr[i])==0xC0) // 110xxxxx
		{
			if (AnsiStr[i+1]=='\0')
			{
				isUTF8=FALSE;
				break;
			}
			if ((0xC0&AnsiStr[i+1])==0x80) // 10xxxxxx
			{
				i+=2;
				continue;
			}
			else
			{
				isUTF8=FALSE;
				break;
			}
		}
		else if((0xF0&AnsiStr[i])==0xE0) // 1110xxxx
		{
			if (AnsiStr[i+1]=='\0')
			{
				isUTF8=FALSE;
				break;
			}
			if (AnsiStr[i+2]=='\0')
			{
				isUTF8=FALSE;
				break;
			}
			if (((0xC0&AnsiStr[i+1])==0x80)&&((0xC0&AnsiStr[i+2])==0x80)) // 10xxxxxx 10xxxxxx
			{
				i+=3;
				continue;
			}
			else
			{
				isUTF8=FALSE;
				break;
			}
		}
		else // 不是UTF-8字符串
		{
			isUTF8=FALSE;
			break;
		}
	}
	return isUTF8;
}
4,908 次浏览 | 没有评论
2010年6月6日 | 归档于 程序
标签: , ,

Ansi to Unicode 1.0发布

一个可用的版本终于出来了。
完成Shift-JIS、GBK、Big5到Unicode的自定义映射表,可以实现这三个编码的文本到Unicode的转换。
对转换cue编码来说,应该还算便利,但仅支持保存为UTF-8编码,余也不打算支持保存为本地编码
功能还很简单,会慢慢完善。下一步工作是利用自定义的这三个映射表判断文本的原始编码,这可能会有点技术

全部源码析出,包括制作映射表文件的代码。

程序及源码下载:http://code.google.com/p/unicue/

欢迎反馈意见。

5,066 次浏览 | 没有评论
2010年6月6日 | 归档于 程序

神奇的无BOM UTF-8文本

先来玩个小把戏(trick)

把这个压缩包test.zip下回来,里面有两个文件,一个是test.txt,另外一个是test.txt.bak,两个文件是一样的,bak只是个备份

用记事本打开test.txt,可以看到里面的内容是这样的:

把“瓧”前面的空格删掉,注意只删一个,表删错了

按下Ctrl+s保存文本,再重新打开,发现变成这样子了

为什么会这么离奇?

再玩多一个小把戏。打开记事本,输入下面两个字,“涓狽”,注意“狽”是繁体

保存,文件名随意。关闭程序重新打开,发现变成了这样

太离奇了,这难道是Windows的bug?其实不是。先补充一句,以上两个把戏仅在Windows简体中文系统有效。这把戏要多少有多少,繁体系统按道理也可以拼凑出来。

这种神奇现象的原因就在于Windows系统对无BOM文本的编码检测。文本文件在ASCII码时代是没有头(header)的,即使到了各多字节编码混战的时代,仍然没有头。其他文件格式,比如bmp,都是有头的,所以Linux系统不需扩展名从header就可以识别文件格式。进入了Unicode时代,文本文件终于有了BOM标记,BOM算得上是文本文件的头。众所周知,Unicode(UTF-16)以小尾(little-endian)顺序存放的BOM是0xFF、0xFE,以大尾(big-endian)顺序存放的BOM是0xFE、0xFF,这两个字节要先于文本写入。UTF-8文本不存在大小尾问题,但微软为UTF-8文本文件默认加上了0xEF、0xBB、0xBF的BOM。

倘若使用UltraEdit将UTF-8文本的BOM剔除,保存后的文件用记事本打开仍然是正常的。也就是说,Windows支持无BOM的UTF-8文本。上面的两个小把戏中的文本的离奇表现都是因为被识别为无BOM的UTF-8文本。Windows对文本编码识别的优先度是:依据BOM>依据UTF-8字符串检测算法>ANSI。

“涓狽”的GBK编码是0xE4B8 0xAA4E,保存为文件再打开时,E4B8AA是“个”的UTF-8编码,4E是“N”的ASCII码,整个字符串通过了检测函数,自然被认为是UTF-8字符串而不是GBK字符串了。

第一个小把戏中,删除空格前整个字符串没有通过检测函数,因此被当成GBK编码;删除空格后,通过了检测函数,因此被当成UTF-8编码。

那到底你想要的是GBK编码呢还是UTF-8编码呢?如果你想要GBK编码方式,“涓狽”这种情况,很遗憾,无论你怎样保存,系统还是会当成UTF-8。虽然这种概率很低,但并不是不存在。当然你将“涓狽”以UTF-8+BOM方式保存,就绝对不会出错了。

选择有BOM的UTF-8还是无BOM的UTF-8,要看场合。Windows默认有BOM,因此编写程序一定要支持BOM,检查前3个字节就行,非常简单。编写的程序保存UTF-8文本,也应当加上BOM,这不是多余,在一些情况下,UTF-8是能被当作多字节编码理解的。php的include文件,如果是UTF-8编码,不能加BOM。余的wordpress后台一直有问题,登录也不正常,原因在于修改某插件的一些代码加入中文字符后保存为UTF-8编码多了BOM。删掉php文件的BOM后就正常了。

6,000 次浏览 | 没有评论
2010年6月3日 | 归档于 技术

一些Android软件

提供一些android软件下载,官方市场上找不到的哦。
安装Barcode Scanner后扫描QR码下载

Chinese Lunar 1.0(农历)

twidroid PRO v.3.1.5

Root Explorer v.2.10.1

Document To Go FULL v2.0(先安装市场的免费版)

国笔输入法

EBAndroid SDK16

MobileQQ for Android到疼迅官网下吧
http://mobile.qq.com/m/HTC/G4

5,801 次浏览 | 1 条评论
2010年5月31日 | 归档于 android

小机器人上安装的软件列表

刷了两次系统。首先是前天在换电池时鬼使神差按住组合键重置了手鸡,通讯录又一次杯具丢失,于是干脆刷到2.1,用起来速度感觉还可以,就是bug太多,设置GPRS上网时死活保存不了设置,后来发现长按“保存”再放开居然保存成功了,今晚听音乐听着听着就不断重启,WIPE了之后依然,没辙只好刷回1.6。

Gmail里的通讯录配合Android的同步很好用,严重推荐。刷回1.6时自动给余把通讯录下回来了(虽然没几条)

不过软件又要重装,烦死了。软件基本都是从market上下,不保存在SD卡里,market里的下载记录会离奇丢失,于是放个列表保存记录,以防不测。

从market安装:
iReader
JLPT Practice
AndroZip File Manager
ColorNote
andLess
Wordpress
Google翻译
PicSay
瀚评英汉词典/汉英词典
Barcode Scanner
子午播放器
ES文件浏览器
ES任务管理器
App to QR
MoreLocale2
Document To Go
Google拼音输入法
Simple Last.fm Scrobbler
OpenWnn plus
ASCII Chart
搜狗手机输入法
RealCalc Scientific Calculator

从SD卡安装:
Chinese Lunar
twidroid PRO
MobileQQ for Android
Root Explorer
Document To Go FULL
国笔输入法
EBAndroid

从此处->http://code.google.com/p/droidapps/downloads/list下载安装(装了Barcode Scanner后放大页面进行扫描):
RMaps

市场上搜不到EBAndroid了

4,614 次浏览 | 没有评论
2010年5月31日 | 归档于 android
标签: , , ,

一个线性时间复杂度的字符编码转换算法

win32 api提供的字符编码转换函数MultiByteToWideChar和WideCharToMultiByte的效率如何不大清楚,一般用途的情况下,字符串不会太长,线性还是O(n^2)还是指数复杂度,时间差是不会感觉出来的。不过这篇博文提出的算法,是确确实实的线性时间复杂度,也即O(n)。n是字符串的长度。余没有读过FireFox的字符转换代码,但应该也是线性时间复杂度。话说回来,这玩意如果能设计成O(n)以上,那还真是活见鬼了。这个算法的意义是,如果你不满意系统提供的转换函数时,可以自己整一套出来。

这个算法已经用在UniCue的Big5ToUnicode程序中。源码下载:http://unicue.googlecode.com/svn/trunk/,装个svn客户端会下得畅快点。

字符编码转换的关键是建立一个字符映射表。比如:

#Big5     Unicode
0x8140 0x4E17
0x8141 0x4E22
0x8142 0x4E2C
0x8143 0x4E55
0x8144 0x4E62
0x8145 0x4E8A

字符映射表可以是一个双向表,通过一方编码查询对方编码。但双向表的效率很低,双向转换还不如分成两个独立的单向转换。
上面的表其实是以Big5为主键的单向表,可以实现Big5到Unicode的查询(反过来查询不现实)。可以将它保存为txt文本,每次转换打开txt查询,不过只有傻子才这样做。

这里是一个相对完全的Big5到Unicode的映射:uao250-b2u.txt,由Mozilla TW提供。Windows系统自带的映射表CP950字符不全,Big5码中的日文假名到Unicode的映射缺失,导致使用MultiByteToWideChar转换或者通过IE转换会丢失字符。

既然不能使用txt文本来查询映射,那如何查询速度才快呢?

如果按Big5码的顺序将对应的Unicode字符全部放置在内存中,起始地址(假设是A)的Unicode字符对应的就是Big5的0x8140,如果来了一个Big5字符假设是0x9C5A,那么它所对应的Unicode字符的内存地址就是A+(0x9C5A-0x8140)*2(注意一个Unicode字符占据两个字节),访问这个地址读取两个Byte就得到Unicode字符。指针加偏移量,访问时间是常数,整个字符串转换不就是线性时间复杂度了嘛。

我们打开uao250-b2u.txt,共19782个Big5字符,最后一个字符0xFEFE,第一个字符0x8140,0xFEFE-0x8140再加1,是32191。等等!这可与文本中的字符数目不吻合啊?原来Big5编码并没有连续的占据0x8140~0xFEFE的全部空间,我们在使用uao250-b2u.txt建立映射表时必须将未定义的空间也全部填写上,映射到Unicode的什么字符其实没关系,不过看到有文章推荐是映射为0xFFFD。

单向映射表可以写代码时就码进去,当然还真有人这么干,也可以保存为硬盘上的一个文件,在进行转换时才读取文件,拷贝文件中的二进制流重建映射表。
下面是生成映射表文件的代码,可以以LittleEndian或BigEndian方式存放Unicode字符,但推荐LittleEndian方式。LittleEndian方式的映射表文件在拷贝到内存时,高低位顺序是正确的,可以直接赋给WCHAR字符。代码需配合Google svn上的uao250-b2u.txt(删除了Mozilla TW版的第一行和最后的空行)使用。输出得到的映射表文件b2u-little-endian.map大小是64382字节。

#include "stdafx.h"
#include <string>
#include <fstream>
#include <iostream>
using namespace std;

unsigned char CharToHex(char ch)
{
	//0-9
	if (ch >= '0' && ch <= '9')
		return (ch - '0');
	//9-15
	if (ch >= 'A' && ch <= 'F')
		return (ch - 'A' + 0xA);
	//9-15
	if (ch >= 'a' && ch <= 'f')
		return (ch - 'a' + 0xA);

	return(0);
}

int _tmain(int argc, _TCHAR* argv[])
{
	string inFilename="uao250-b2u.txt";
	ifstream infile(inFilename.c_str());
	if (!infile)
	{
		cerr<<"Unable to open b2u.txt!\n";
		return -1;
	}

	ofstream outfile_littleendian("b2u-little-endian.map",ios::binary);
	if (!outfile_littleendian)
	{
		cerr<<"Can not open b2u-little-endian.map!\n";
		return -1;
	}

	char LITTLEENDIANBOM[2]={'\xFF','\xFE'};
	//outfile_littleendian.write(LITTLEENDIANBOM,2);

	/*
	ofstream outfile_bigendian("b2u-big-endian.map",ios_base::binary);
	if (!outfile_bigendian)
	{
		cerr<<"Can not open b2u-big-endian.map!\n";
		return -1;
	}
	char BIGENDIANBOM[2]={'\xFE','\xFF'};
	outfile_bigendian.write(BIGENDIANBOM,2);
	*/

	string str;
	int i=0;
	int offset=0x8140;
	char zero[2]={'\xFD','\xFF'};
	while(getline(infile,str))
	{
		i++;

		//读取Big5码位
		int Big5offset;
		Big5offset=CharToHex(str[5])+CharToHex(str[4])*16+CharToHex(str[3])*16*16+CharToHex(str[2])*16*16*16;

		while(offset!=Big5offset)
		{
			offset++;
			outfile_littleendian.write(zero,2);
		}
		offset++;

		//注意Unicode字符在内存中是先低位后高位
		//写入映射表可以以big_endian或little_endian方式
		//推荐采用little_endian方式

		unsigned char HighByte,LowByte;
		HighByte=CharToHex(str[9])*16 +CharToHex(str[10]);
		LowByte=CharToHex(str[11])*16+CharToHex(str[12]);

		if ((HighByte>255)||(LowByte>255))
			cerr<<"Error occur in Line "<<i<<"!\n";

		outfile_littleendian.write((char*)&LowByte,1);
		outfile_littleendian.write((char*)&HighByte,1);

		/*
		unsigned char bigEndian[2];
		bigEndian[0]=CharToHex(str[9])*16 +CharToHex(str[10]);
		bigEndian[1]=CharToHex(str[11])*16+CharToHex(str[12]);
		outfile_bigendian.write(bigEndian,2);
		*/
	}
	cout<<i<<" lines done!\n";
	infile.close();
	outfile_littleendian.close();
	//outfile_bigendian.close();

	return 0;
}

自定义的字符映射表这样就创建完毕啦。使用就很简单了。
使用了MFC中的CString和CFile的转换函数,当然也可以不用MFC这一套
Big5toUnicode中的b2u-CString.h:

//big5 to Unicode转换函数
#pragma once
#include "stdafx.h"

#define BIG5OFFSET 0x8140

//big5Str最后一个字符为终止符
CString Big5ToUnicode(const char* big5Str,UINT length)
{
	CString unicodeStr;
	unicodeStr=_T("");

	if (!big5Str)
		return unicodeStr;

	if (length==0)
		return unicodeStr;

	TCHAR path[MAX_PATH]; //最长260
	GetModuleFileName(NULL, path, MAX_PATH);
	CString mapPath=CString(path);

	int position=mapPath.ReverseFind('\\');
	mapPath=mapPath.Left(position);
	mapPath+=_T("\\b2u-little-endian.map");

	//加载映射表
	CFile loadMap;
	if (!loadMap.Open(mapPath,CFile::modeRead))
	{
		loadMap.Close();
		::AfxMessageBox(_T("Big5toUnicode map loading error!"));
		return unicodeStr;
	}

	UINT mapLength=loadMap.GetLength();
	char *mapBuffer=new char[mapLength];
	loadMap.Read((void *)mapBuffer,mapLength);
	loadMap.Close();

	//转换
	unsigned char low=0,high=0;
	TCHAR chr=0;
	for (UINT i=0;i<length;)
	{
		//big5码的字符是先高位后低位
		//高位至少从0x81起
		//big5Chr[1]:高位
		//big5Chr[0]:低位
		memcpy(&high,big5Str+i,1); //读取第一个byte
		i++;
		if (high>0x80) //第一个byte是高位
		{
			memcpy(&low,big5Str+i,1); //读取低位
			i++;
		}
		else
		{
			low=high;
			high=0;
		}

		chr=low+high*256;
		if (chr<BIG5OFFSET)
		{
			unicodeStr.AppendChar(chr);
		}
		else
		{
			int offset;
			offset=chr-BIG5OFFSET;
			memcpy((void*)&chr,mapBuffer+offset*2,2);
			unicodeStr.AppendChar(chr);
		}
	}

	delete []mapBuffer;
	mapBuffer=NULL;

	return unicodeStr;
}

可执行的测试程序在此:http://code.google.com/p/unicue/downloads/list

6,660 次浏览 | 1 条评论
2010年5月26日 | 归档于 程序

cnBeta上流行的菊花文真相

时常看到cnBeta上有银用菊花文回复,比如这里,很是好奇。

下面是一段菊花文:

这҉是҉一҉段菊҉花҉文҉

这是真相:

很简单, ҉ 是一个Unicode的字符,值是0x0489,大概是规定了不占据一个字符位置,与它的前一个字符重叠在一起
制作菊花文也就很简单了,在正常字符串中逐字插入这个菊花符,可以拷贝粘贴的

菊花文的一个作用是避免敏感词检查,比如cnBeta这种地雷遍布的地方和某些场合
很邪恶的一种文体

4,116 次浏览 | 没有评论
2010年5月20日 | 归档于 技术
标签: ,

UniCue项目始动

UniCue项目正式启动。

很久以前就想做一个编码转换工具。在xp时代用apploc+记事本配合转换cue还算便捷,但到了win7 apploc失效。某萌烈弄了一个FixCue,成为余win7转换cue居家必备之工具,但估计某烈也只是弄了Shift-JIS到utf-8的转换。GBK的cue可用记事本直接打开再utf-8另存,只因为中文简体windows默认Codepage是CP936。如果遇到呆丸的自抓,一般都会以big5编码保存cue,一点办法都没,微软的CP950不含日文假名到Unicode的映射,导致日文假名在转换过程中丢失。很多转换程序都是使用win32 api,如Notepad++,存在同样问题。

简体中文系统里只好自己编程解决。不用系统的codepage,意味着要自己制作字符映射表和编写转换函数。这里面涉及到一些设计策略,放在以后的博文再谈。

经过一晚上的奋斗,终于成功实现BIG5码到Unicode的转换。Unicode到UTF-8使用win32 api或者自写函数都很简单。

籍此一年前就计划的UniCue正式启动。预览版已经发布,代码放在Google上托管:http://code.google.com/p/unicue/

7,029 次浏览 | 2 条评论
2010年5月15日 | 归档于 程序

wordpress博客配置随机banner图片

调教wordpress随机banner图片成功。

随机图片由php代码产生。策略是返回header信息将自身表现得像一个图片文件,接着是指定目录下的一随机图片的数据流。

headerimages.php,支持图片格式可参考自行添加

<?php

$url='./headerimages';

$files=array();
if ($handle=opendir("$url")) {
    while(false !== ($file = readdir($handle))) {
          if ($file != "." && $file != "..") {
          if(substr($file,-3)=='gif' || substr($file,-3)=='jpg' || substr($file,-3)=='png') $files[count($files)] = $file;
          }
    }
}
closedir($handle);

$random=rand(0,count($files)-1);
if(substr($files[$random],-3)=='gif') header("Content-type: image/gif");
elseif(substr($files[$random],-3)=='jpg') header("Content-type: image/jpeg");
elseif(substr($files[$random],-3)=='png') header("Content-type: image/png");
readfile("$url/$files[$random]");

?> 

对于blocks主题,修改style.css中的样式即可,添加背景图片url为上面php代码保存后的php文件路径,并将高度修改为100px。(因此banner图片的像素是960×100)

style.css

#header .content {
background:#FFF url(headerimages.php);
width:100%;
height:100px;
}

就这么简单。将headerimages.php上传到wp-content\themes\blocks目录下,并且新建存放banner的文件夹headerimages。

余同时也修改了blocks主题的header.php,使header右上显示页面、左下显示分类。
随机banner(含30P图片)+header.php打包:blocks-randombanner.zip,仅使用随机banner不需覆盖header.php

5,479 次浏览 | 没有评论
2010年4月30日 | 归档于 技术