東京に自分で暮らすことが始まります

28号下午抵达东京。自己在东京的生活正式开始。

这两天的见闻和印象:
机场:成田机场离市区非常远,坐机场大巴返回后乐园的DOME HOTEL花了两个小时,3000日元,稍贵了点。大巴非常非常安静
地铁:线内160,转换一次线路只用加30,余的出行还没有超过190日元的。地铁卡非常便利,但持卡不会打折,卡工本费是500日元。电车上也很安静,偶见年轻人打电话。
自动扶梯:靠左站立,留出右边通道给赶时间的人
饮食:饮料100~200日元之间,基本上是自动贩卖机上购买。吃饭:大多数店需要在店外的自动贩卖机购买食券,再在店内交给店员,KFC则和国内一样。吃饭的价格基本在300~1000日元之间。不知道是否面条都叫拉面,昨晚吃的拉面明显不是手工拉制的。盖饭之类的定食大概在500日元上下
购物:住所靠近池袋,两天的购物都是前往池袋。布团套装花了1W,唔~好贵。池袋的Sunshine City有个百元店,在那扫了一堆日常生活日品,直到拿不动。在3coins买了把长伞。日本人好像都喜欢拿长伞,说是折伞使用后容易弄湿手,一般在玄关处也都有放置长伞的架子。在ビックカメラ买了个电源转换接头,国内的三线电源插头拿到日本都不能直接使用,圆头的插头也不行。
住:礼金平摊后每个月需要支付8w多日元,网络无料(光速中),水电费另算。屋租是最大的一笔支出。小公寓内装修还不错,设备很齐全,电视机古老了一点,居然集成录像带播放功能,而且只有12个频道。洗衣服要投币,有点仆街。由于靠近大马路,稍有点吵。
街道:非常干净,虽然说不上完全没有垃圾,道路和行人道都是柏油铺砌。行人和汽车都很遵守交通规则,车会让人,以致过小路口的斑马线时还停留下来让车先过。很容易迷路,特别是晚上。日本的学校放学相当早,三点多就能见到穿着校服的中学生,而且到了晚上还能大量见到(放学后不许穿着校服游荡是哪里的规定啊?)。不能不说,泥轰精穿着真的有泥轰的气质,今天数次被人一眼认出是中国人
受姬:杯具中,没有银行卡不能办理。软银的店倒是到处都有,iphone4很帅,把玩了一下,iphone4需要预约,2周后能取。不过想买HTC Desire,同样需要预约。不知受姬何时能入手
银行卡:杯具中,卡好像要两周后才能发下来
外国人登陆证:住的文京区的役所在后乐园附近,手续费300,同样需要等两周才能取。后乐园的遊園地貌似很好玩,过山车溜过,下面的店仿佛地震中

两天花掉了4w日元。

灭有受姬,相机又留给家里了,不能上图稍微残念了点

2,315 次浏览 | 没有评论
2010年9月29日 | 归档于 私语
标签:

ANSI2Unicode 1.0.3正式版发布

ANSI2Unicode是开源项目UniCue的组成部分。ANSI2Unicode致力于文档编码的转换,通过自定义编码映射表,目前支持Shift-JIS、GBK、BIG5三种编码到utf-8的转换。

ANSI2Unicode 1.0.3 功能特性:
–完整的Unicode支持,不必为文档路径中的特殊字符担心
–自定义BIG5 to Unicode的映射表,取代微软自身的CP950,解决日文假名丢失问题
–转换结果保存为utf-8编码的文本
–自动检测文本编码
–支持文件拖放操作及命令行打开文档
–自动修正cue中的File行音频文件扩展名
–自动修正cue中的File行旧式“The True Audio”标签为“WAVE”
–不改变默认打开程序的txt/log/cue右键菜单关联
–提取tak/flac/ape的内嵌cue,并自动修正cue中的音频文件名
–转换字符串功能
–转换字符串模式下,支持拖放乱码文件名进行转换

下载:http://code.google.com/p/unicue/

源码:http://unicue.googlecode.com/svn/trunk/

bug反馈或特性提出可直接在blog回复或前往http://code.google.com/p/unicue/issues/list

转载扩散大欢迎

11,644 次浏览 | 5 条评论
2010年9月11日 | 归档于 程序

flac的内嵌cue提取

flac的内嵌cue实际上有两个存放位置。按照flac官方的技术文档,flac的文件结构中METADATA_BLOCK可以存放CUESHEET类型的数据,不过这种形式的内嵌cue相当复杂,余抛弃实现提取这种cue了。另外METADATA_BLOCK还可以存放VORBIS_COMMENT类型的数据,数据部中“cuesheet=”后面的即是cue,foobar2000调用flac.exe进行编码并写入内嵌cue时,是写到VORBIS_COMMENT中。存放到VORBIS_COMMENT中似乎更普遍一点。

如果不按照flac的文件结构,用稍微暴力一点的方法提取,也是可以的,这时就和提取tak的内嵌cue非常类似了,只不过flac的位于文件头部。

简单说一下flac的METADATA_BLOCK的结构。一个METADATA_BLOCK由两部分组成,METADATA_BLOCK_HEADER和METADATA_BLOCK_DATA。METADATA_BLOCK_HEADER为固定的四字节(32bit),第一个bit为判断是否是最后一个METADATA_BLOCK,第二至第八bit为METADATA_BLOCK的类型,VORBIS_COMMENT的类型为4,后面的3个字节是METADATA_BLOCK_DATA的长度。

因为只注重内嵌cue,所以只有遇到VORBIS_COMMENT类型,才开始进行cuesheet的捕捉,否则一直跳过。跳过时文件指针移动的偏移量即是METADATA_BLOCK_DATA的长度。

代码:

    CFile OpenFile;
    if (!OpenFile.Open(FlacPathName,CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary))
    {
        OpenFile.Close();
        ::AfxMessageBox(_T("打开失败!"),MB_OK);
        return FALSE;
    }

    if (OpenFile.GetLength()<1048576) // 小于1M,文档太小了
    {
        OpenFile.Close();
        return FALSE;
    }

    unsigned char Header[5];
    memset(Header,0,5);
    ULONGLONG position=0;
    //4个字节的头部
    OpenFile.Read((void*)Header,4);
    if (strcmp((char*)Header,"fLaC")!=0)
    {
        AfxMessageBox(_T("Not real flac file!"));
        return FALSE;
    }

    unsigned char chr;
    unsigned char *Buffer=NULL;
    UINT Length;
    //4个字节的METADATA_BLOCK_HEADER
    do
    {
        OpenFile.Read((void*)Header,4);
        //解析
        memcpy(&chr,Header,1);
        //检查最高位是否为1
        if ((chr&0x80)==0x80)
        {
            //最后一个METADATA_BLOCK
            if ((chr&0x7F)==0x04)//是VORBIS_COMMENT
            {
                //读取BLOCK长度
                Length=Header[1]*0x10000+Header[2]*0x100+Header[3];
                //申请空间
                Buffer=new unsigned char[Length+1];
                //读取BLOCK DATA
                OpenFile.Read((void*)Buffer,Length);
                Buffer[Length]='\0';
            }
            break;
        }
        else
        {
            //不是最后一个METADATA_BLOCK
            if ((chr&0x7F)==0x04)//是VORBIS_COMMENT
            {
                //读取BLOCK长度
                Length=Header[1]*0x10000+Header[2]*0x100+Header[3];
                //申请空间
                Buffer=new unsigned char[Length+1];
                //读取BLOCK DATA
                OpenFile.Read((void*)Buffer,Length);
                Buffer[Length]='\0';
                break;
            }
            else //不是VORBIS_COMMENT
            {
                //读取BLOCK长度
                Length=Header[1]*0x10000+Header[2]*0x100+Header[3];
                //移动文件指针
                OpenFile.Seek(Length,CFile::current);
                position=OpenFile.GetPosition();
            }
        }
    } while(position<=1048576);

    OpenFile.Close();
    if (!Buffer)
        return FALSE;

    //查找 Cuesheet 标记,自动机模型,大小写不敏感
    int state=0,BeginPos=0,EndPos=0;
    for (UINT i=0;i<Length;++i)
    {
        if ((Buffer[i]>=0x41)&&(Buffer[i]<=0x5A))
            Buffer[i]=Buffer[i]+0x20;

        switch (Buffer[i])
        {
        case 'c':
            state=1;      //C
            break;
        case 'u':
            if (state==1)
                state=2;  //Cu
            else
                state=0;
            break;
        case 'e':
            switch (state)
            {
            case 2:
                state=3;  //Cue
                break;
            case 5:
                state=6;  //Cueshe
                break;
            case 6:
                state=7;  //Cueshee
                break;
            default:
                state=0;
            }
            break;
        case 's':
            if (state==3)
                state=4;  //Cues
            else
                state=0;
            break;
        case 'h':
            if (state==4)
                state=5;  //Cuesh
            else
                state=0;
            break;
        case 't':
            if (state==7)
            {
                state=8;  //Cuesheet
            }
            else
                state=0;
            break;
        default:
            state=0;
        }
        if (state==8)
        {
            BeginPos=i+2;
            break;
        }
    }
    if (BeginPos==0)
    {
        delete []Buffer;
        return FALSE;
    }
    //查找终止符 0D 0A ? 00 00 00(连续3个终止符以上)
    state=0;
    for (int i=BeginPos;i<20480;++i)
    {
        switch (Buffer[i])
        {
        case '\0':
            state++;
            break;
        default:
            state=0;
        }
        if (state==3)
        {
            EndPos=i-3; //指向0D 0A后的第一个字符
            break;
        }
    }

    if (EndPos<=1)
    {
        delete []Buffer;
        return FALSE;
    }

    if ((Buffer[EndPos-2]=='\x0D')&&(Buffer[EndPos-1]=='\x0A'))
        EndPos--;

    int CueLength=EndPos-BeginPos+1;
    if (CueLength<=10) //too short
    {
        delete []Buffer;
        return FALSE;
    }

    unsigned char *CueString;
    CueString=new unsigned char[CueLength+1];
    memcpy(CueString,Buffer+BeginPos,CueLength);
    CueString[CueLength]='\0';

    delete []CueString;
    delete []Buffer;
    return TRUE;

CueString中存放的即是utf-8编码的cue字符串,写入到文件即可。
捕捉cuesheet标记的方法和tak一样的。

5,214 次浏览 | 没有评论
2010年9月9日 | 归档于 技术, 程序
标签: , , , , ,

tak的内嵌cue提取

tak的内嵌cue提取非常简单。内嵌的cue位于文件尾部,以utf-8编码存放。只要捕捉到起始点和结束点,拷贝出来即可。
内嵌cue的起始标记是Cuesheet,大小写不敏感,结束标记是之后遇到的第一个连续终止符串(六个终止符以上)
为了不用读取整个tak文件,将文件指针移到最后20K处,将之后的数据拷贝出来,在这20K的数据中搜索。一般的cue也不会有那么长吧

	CFile OpenFile;
	if (!OpenFile.Open(TakFilePathName,CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary))
	{
		OpenFile.Close();
		::AfxMessageBox(_T("打开失败!"),MB_OK);
		return FALSE;
	}

	if (OpenFile.GetLength()<20480) // 小于20K,文档太小了
	{
		OpenFile.Close();
		return FALSE;
	}

	OpenFile.Seek(-20480,CFile::end);
	unsigned char Buffer[20480]; //20k的缓冲区
	memset(Buffer,0,20480);
	OpenFile.Read(Buffer,20480);
	OpenFile.Close();

	//查找 Cuesheet 标记,自动机模型,大小写不敏感
	int state=0,BeginPos=0,EndPos=0,Length=0;
	for (int i=0;i<20480;++i)
	{
		if ((Buffer[i]>=0x41)&&(Buffer[i]<=0x5A))
			Buffer[i]=Buffer[i]+0x20;

		switch (Buffer[i])
		{
		case 'c':
			state=1;      //C
			break;
		case 'u':
			if (state==1)
				state=2;  //Cu
			else
				state=0;
			break;
		case 'e':
			switch (state)
			{
			case 2:
				state=3;  //Cue
				break;
			case 5:
				state=6;  //Cueshe
				break;
			case 6:
				state=7;  //Cueshee
				break;
			default:
				state=0;
			}
			break;
		case 's':
			if (state==3)
				state=4;  //Cues
			else
				state=0;
			break;
		case 'h':
			if (state==4)
				state=5;  //Cuesh
			else
				state=0;
			break;
		case 't':
			if (state==7)
			{
				state=8;  //Cuesheet
			}
			else
				state=0;
			break;
		default:
			state=0;
		}
		if (state==8)
		{
			BeginPos=i+2;
			break;
		}
	}

	if (BeginPos==0)
		return FALSE;

	//查找终止符 0D 0A ? 00 00 00 00 00 00 (连续六个终止符以上)
	state=0;
	for (int i=BeginPos;i<20480;++i)
	{
		switch (Buffer[i])
		{
		case '\0':
			state++;
			break;
		default:
			state=0;
		}
		if (state==6)
		{
			EndPos=i-6; //指向0D 0A后的第一个字符
			break;
		}
	}

	if (EndPos<=1)
		return FALSE;

	if ((Buffer[EndPos-2]=='\x0D')&&(Buffer[EndPos-1]=='\x0A'))
		EndPos--;

	Length=EndPos-BeginPos+1;
	if (Length<=10) //too short
		return FALSE;

	unsigned char *CueString;
	CueString=new unsigned char[Length+1];
	memcpy(CueString,Buffer+BeginPos,Length);
	CueString[Length]='\0';

	delete []CueString;
	return TRUE;

CueString中存放的即是utf-8编码的cue字符串,写入到文件即可。
这自动机模型捕捉Cuesheet写得很优美(擦汗

4,694 次浏览 | 没有评论
2010年9月6日 | 归档于 技术, 程序
标签: , , , , ,

tinyXml处理UTF-8编码详解——写入和读取

以前写过一篇博文介绍tinyXml输出utf-8文档

tinyXml的特点是不对xml节点内容的具体编码处理,这一切都交给用户。因此tinyXml和字符有关的函数都是只接受char*的数据类型。
例如:

TiXmlElement *pRoot=new TiXmlElement("test");
pRoot->SetAttribute("name","名字");

上述代码产生的节点,如果用TiXmlDocument的SaveFile函数直接保存,只能是ANSI的本地编码(无论程序是否是unicode),即使TiXmlDeclaration指定为utf-8。一种方法是输出到TiXmlPrinter,将TiXmlPrinter.CStr()转换到utf-8编码的char*后保存。

char*在双字节编码下是一种很奇特的字符串,中文平台下的VC的编译器,char*可以存放GBK汉字,编译能正确识别字符,因为ASCII码的最高位为0,而GBK双字节字符的首字节最高位为1。

在使用utf-8字符串时,必须树立一个观念:utf-8应当只在传输时使用,不适合作为函数过程的处理对象。什么是传输场合?网络传输和文件读写。以文件读写为例,文件以utf-8编码存放,在读入到内存后,应当立刻转换为unicode宽字符串。程序的内部处理过程中只有unicode宽字符串。直到写入文件时,unicode宽字符串才转换为utf-8字符串。

utf-8字符串本身是变长字符串,并没有特定的数据类型。它是以char*形式存放,它的byte的表现不符合任何双字节编码,当成双字节编码处理会立刻出错。事实上,char*只是一个存放空间,用void*、unsigned char*本质上没有区别。(倘若你喜欢,甚至可以拿char*来存放unicode宽字符串,一次memcpy两个byte就是了)。

脱离双字节编码(如GBK)的tinyXml使用方法是存在的。
例如上述代码可以改为:

TiXmlElement *pRoot=new TiXmlElement("test");
CStringA UTF8Str=CW2A(L"名字",CP_UTF8);
pRoot->SetAttribute("name",UTF8Str);

UTF8Str变量名即是内含的char*字符串的起始指针。CW2A函数可以自己写一个代替,并不难实现。此时可以直接调用TiXmlDocument的SaveFile函数保存为无BOM的UTF-8文档。要保存为含BOM的UTF-8文档,仍然需要TiXmlPrinter,但此时不需要对TiXmlPrinter.CStr()进行任何处理。

XmlEntityTree=new TiXmlDocument;
TiXmlDeclaration *dec=new TiXmlDeclaration("1.0","utf-8","");
XmlEntityTree->LinkEndChild(dec);
TiXmlElement *pRoot=new TiXmlElement("test");
CStringA UTF8Str=CW2A(L"名字",CP_UTF8);
pRoot->SetAttribute("name",UTF8Str);
XmlEntityTree->LinkEndChild(pRoot);
TiXmlPrinter printer;
XmlEntityTree->Accept(&printer);

char UTF8BOM[3]={'\xEF','\xBB','\xBF'};

CFile theFile;
theFile.Open(_T("test.xml"),CFile::modeCreate|CFile::modeWrite);
theFile.Write(UTF8BOM,3);
theFile.Write(printer.CStr(),strlen(printer.CStr()));
theFile.Close();

tinyXml在加载xml文档时有一个标记,TiXmlDocument.LoadFile(TiXmlEncoding encoding);
这个标记没多大作用,无论设为TIXML_ENCODING_UTF8还是TIXML_ENCODING_LEGACY,读入的节点的数据类型一样是char*。
设为TIXML_ENCODING_UTF8标记的唯一作用是tinyXml会自动处理文档的BOM。

对于下面文档,怎样才能正确读取到TemplateStr节点的内容?很简单,在读取时进行转换就行。

<?xml version="1.0" encoding="utf-8" ?>
<config>
    <TemplateStr>中文</TemplateStr>
    <AutoFixCue>true</AutoFixCue>
    <AutoFixTTA>true</AutoFixTTA>
    <AcceptDragFLAC>true</AcceptDragFLAC>
    <AcceptDragTAK>true</AcceptDragTAK>
    <AcceptDragAPE>true</AcceptDragAPE>
</config>
TiXmlDocument *xmlfile= new TiXmlDocument(FilePath);
xmlfile->LoadFile(TIXML_ENCODING_UTF8);

TiXmlHandle hRoot(xmlfile);
TiXmlElement *pElem;
TiXmlHandle hXmlHandle(0);

//config节点
pElem=hRoot.FirstChildElement().Element();
if (!pElem) return FALSE;
if (strcmp(pElem->Value(),"config")!=0)
	return FALSE;

//TemplateStr节点
hXmlHandle=TiXmlHandle(pElem);
pElem=hXmlHandle.FirstChild("TemplateStr").Element();
if (!pElem) return FALSE;
CString TemplateStr=UTF8toUnicode(pElem->GetText());

UTF8toUnicode函数:

CString UTF8toUnicode(const char* utf8Str,UINT length)
{
	CString unicodeStr;
	unicodeStr=_T("");

	if (!utf8Str)
		return unicodeStr;

	if (length==0)
		return unicodeStr;

	//转换
	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 if() // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
		{}
		else if() // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx  10xxxxxx
		{}
		else if() // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx  10xxxxxx 10xxxxxx 
		{}
		*/
		else // 不是UTF-8字符串
		{
			return unicodeStr;
		}
		unicodeStr.AppendChar(chr);
	}

	return unicodeStr;
}

CString UTF8toUnicode(const char* utf8Str)
{
	UINT theLength=strlen(utf8Str);
	return UTF8toUnicode(utf8Str,theLength);
}

strlen取char*的长度等于字节数(不含终止符),不是utf-8字串的真正字符个数。

11,818 次浏览 | 没有评论
2010年8月28日 | 归档于 技术, 程序

自制WordPress标签云页面

侧栏的标签云中的tag数量毕竟有限。于是很久前就想自制一个标签云页面。

复制所用主题下的page.php,命名tags.php;
在tags.php头部添加

<?php
/*
Template Name: tags
*/
?>

将tags.php中的<?php the_content(); ?>替换为<?php wp_tag_cloud(‘smallest=12&largest=40&unit=px&number=500’);?>
保存,上传至所用主题下的目录

后台添加新页面,模板选用tags。OVER

wp_tag_cloud是wp自身提供的函数,smallest是最小的字号(相关主题数最少),largest是最大的字号(相关主题数最多),unit是单位,number是显示的tag的最大数量

但对于blocks主题,还需要指定新的样式表。默认的content样式tag由于大小不一会重叠。
在style.css添加新样式

.tags-cloud{
	background:#FFF;
	padding:10px 11px 10px;
	overflow:hidden;
}

将wp_tag_cloud所在的div的class修改为tags-cloud。

余因为关闭了评论,所以同时移除了和评论相关的代码
最终的tags.php

<?php
/*
Template Name: tags
*/
?>

<?php get_header(); ?>

<?php if (have_posts()) : the_post(); ?>
	<div class="post">
		<h3 class="title"><?php the_title(); ?></h3>

		<div class="tags-cloud">
			<?php wp_tag_cloud('smallest=14&largest=46&unit=px&number=500');?>
			<div class="fixed"></div>
		</div>

		<div class="meta">
			<div class="act">
				<?php edit_post_link(__('Edit', 'blocks'), '', ''); ?>
			</div>
			<div class="info">
				<?php
					if (function_exists('the_views')) the_views(true, '', '');
				?>
			</div>
			<div class="fixed"></div>
		</div>
	</div>

<?php else: ?>
	<div class="block">
		<div class="content small r">
			<?php _e('Sorry, no posts matched your criteria.', 'blocks'); ?>
		</div>
	</div>

<?php endif; ?>

<?php get_footer(); ?>
3,399 次浏览 | 没有评论
2010年8月21日 | 归档于 技术

修正统计插件statpresscn的一个写UTF-8数据的错误

博客的访问统计插件使用的是statpresscn,从一开始使用163的api解析ip地址,就发现写入会出错,当时不以为意,随便改为其他的api(返回的地址一般都是空串)。直到昨天小烈留言,才发现原来用户名为中文字符同样也会出错。于是决心解决它。

WordPress database error: [Incorrect string value: ‘\xE7\x83\x88\xE4\xB9\x8B…’ for column ‘user’ at row 1]
INSERT INTO blog_wp_statpressCN (date, time, ip, urlrequested, statuscode, ptype, pvalue, agent, referrer, search,nation,os,browser,searchengine,spider,feed,user,timestamp) VALUES (‘20100820′,’00:25:24′,’125.46.23.194′,’/index.php/gbook’,”,’page’,’103′,’Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 (.NET CLR 3.5.30729)’,’https://kuyur.info/blog/index.php/archives/category/ongaku’,”,”,’Windows XP’,’Firefox 3′,”,”,”,’烈之斩’,’1282263924′)

从小烈反馈以及余自测的错误来看,是UTF-8字符要写入数据库时出错。访客在留言后,cookies自动记录用户名,访客再访问页面,statpresscn这时候采集的不再是ip,而是用户名。“\xE7\x83\x88\xE4\xB9\x8B”是“烈之”的UTF-8编码。余的博客是UTF-8编码,表单提交到mysql的字符类型也是UTF-8,但博文写得好好的,没理由是服务器那边的错误。将数据库进行备份,下载回来一看,发现表blog_wp_statpress的CHARSET居然是latin1。错误正是出在这里。(建议statpresscn作者在创建数据表时按照blog设定的编码创建)

修改表的字符集、user字段的CHARACTER SET为UTF-8,用中文用户名留言后的错误消失。将解析ip的api改回163,修改nation字段的CHARACTER SET为UTF-8,博文最开始提到的错误也消失。

以防万一,将tinytext和text类型的字段都改成了UTF-8。

ALTER TABLE blog_wp_statpress default CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify user tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify urlrequested text CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify agent text CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify referrer text CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify search text CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify nation tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify os tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify browser tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify searchengine tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify spider tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify feed tinytext CHARACTER SET utf8;
ALTER TABLE blog_wp_statpress modify ptype tinytext CHARACTER SET utf8;

可以将上述sql语句保存sql文件,在mysql中用source命令执行。

5,705 次浏览 | 1 条评论
2010年8月20日 | 归档于 技术
标签: ,

アカイイト 游戏脚本文件分析及脚本提取

アカイイト,中文译名红线,success社05年出品的PS2平台上的和风百合游戏。音乐由Little WING负责,神作。
这是官网:http://www.success-corp.co.jp/software/ps2/akaiito/

红线的脚本文件是PS2光盘或者镜像里的SCRIPT\SCRIPT.AFS文件。其余文件的内容凭文件名也很好猜。BGM.AFS是背景音效,CELDATA.AFS是CG,VOICE.AFS是语音。NULL.000和NULL.001则是两个垃圾文件,每个大小512M,倘若制作汉化镜像应当去掉。

SCRIPT.AFS中的脚本文字是按SHIFT-JIS编码以非加密方式存放
下载地址:http://unicue.googlecode.com/svn/trunk/Files/Akaiito/SCRIPT.AFS
下载回来后可以用notepad++打开,就能发现脚本是一段一段的嵌套在非脚本字符中,非脚本字符有很多是不能显示的ASCII控制字符,并有大量0x00也即终止符,因此刚开始时不能当作字符串来处理。

不过脚本字符和干扰字符所属区段不重合,因此非常适合编写程序来处理,差不多六个函数搞定。

第一步,剔除控制符等干扰字符。
将整个文件都当成SHIFT-JIS编码的文本进行检测,原理和GBK、Shift-JIS、BIG5编码检测算法提及的检测算法是非常像的,只不过这里的检测遇到异常字符不终止继续而已。
剔除策略是,遇到0x00~0x1F用空格替代;遇到0x7F,用空格替代;半角假名区的字符事实上没有出现(倘若余错了,请联系纠正);遇到0x8000~0x813F,用两个空格代替;遇到SHIFT-JIS中0x8140~0xFFFF中不存在的字符(使用到了映射表文件jis2u-little-endian.map进行检查),用两个空格取代。

在第一步后,文本已经是一个正经的字符串,这时候可以用正则表达式进行后续处理。

第二步,删除$w、$s、单个ASCII可见字符(前后均有空格),有限状态自动机模型。第二步后文本已经基本可读。

第三步,需要将文本分割成章节标题和脚本内容两部份分别处理。#cr0在游戏中是回车的转义符,章节标题字串的#cr0是替换为换行,脚本内容字串的则是删除掉(脚本内容字串的最后部分需要灵活处理)。最后的工作是将长空格串(三个连续空格以上)替换为换行。自写代码的处理到此结束。在去除空格串后文本由6M多缩小到1M多。

第四步是合并文本,并将SHIFT-JIS编码转换为UTF-8(使用ANSI2Unicode,本想调用toUnicode.h实现的),用notepad++的正则表达式替换功能删除数字编号(应该不会删错吧?自写程序会鉴别前后是否有换行。偷懒了),并将多余的回车换行删掉,最后剔除少量错误。

step3的脚本:http://u.115.com/file/f0f1b09c22,未删除数字的或许对分章有意义

程序:http://unicue.googlecode.com/svn/trunk/

最终脚本在此:akaiito_script_step4.pdf
共计57万字

6,610 次浏览 | 7 条评论
2010年8月20日 | 归档于 程序, 红线

玩红线学日语(一):アカイイト -それは物語の予兆-

一緒に日本語を勉強しましょう。

日语学到现在,感觉词汇成为瓶颈。如何记住海量的词汇及其准确的假名拼写,是余目前遇到的大难题。拼写中的长音和促音让余很想撞墙。但好像又没有太好的方法。传说如果能一直反复练习,比如和日本人文字聊天之类的,很快就记住了。每天将一篇文章打几十遍也是一种反复练习,游戏脚本或轻小说是一个比较好的对象。

前段时间将红线(アカイイト)的游戏脚本提取并整理了出来,只剩下章节结构未明。除开一些生僻词汇,红线脚本的生词还是很日常的,适合练习。

第一课:それは物語の予兆。对象:三级以上(具备一定语法基础)

―――――――――――――――――――――――
◇それは物語の予兆
―――――――――――――――――――――――
木。
たくさんの木。
高く伸びる木々それぞれが、好き放題に枝を伸ばし、空のほとんどを覆い隠してしまっている。

深い――林か森か。幹の太い古木が立ち並び、たけのある草が生い茂る狭い道を、わたしは走っている。
――――
――振り返る。

瓦の並ぶ屋根が見えた。
時代劇で見るような、立派な構えの門はないけれど、それは見事なお屋敷だった。
平屋の大きな日本家屋。
離れに見えるのは蔵かもしれない。
こういう屋敷に住んでいる人たちを、由緒正しい旧家とでも言うんだろうか。

涼しげな鈴の音に、わたしは前へと向き直る。

ぐっと誰かに手を引かれ、わたしはさらに足を速める。

道の勾配がだんだん急になる。
ああ、ここは山なんだ。
手を引かれるままに、わたしは山道を登る。

舗装されていなかったとはいえ、まだ道らしい道だった道を外れて、わたしたちは草を分けるようにして進む。

速く、早く、はやく、はやく――
わたしを引く手が強くなる。
誰かに追いかけられでもしているのか。

何をそんなに急いでいるんだろう。

足元の草を踏みしめて急ぐ。

ざっ、ざっ、ざっ、ざっ。

ざあっ――

急に視界が開けた。
おそらく山の中腹あたり。
そこには見上げるほどの大きな――
数百の年月を雨風とともに過ごしたといった趣のある、大きな大きな木が根を下ろしていた。
他の木はこの木に遠慮しているのか、あたりは少し開けている。

ざあっ――
通り過ぎる風に、木に咲く花がゆらりと揺れ。
この景色には見覚えがあった。
そこがどこかは知らないけれど。
テレビか映画で見たんだろうか。
いや、違う。
そうじゃないということは、わかっていた。
これは。
この景色は。
これはわたしの――

『たいせつなひとが、いなくなってしまった』

風にもがれた花びらが、蝶のようにひらひらと舞っている。
奇妙な既視感と喪失感。
何だろう、この感覚は。
そして今まで気にしていなかったけど。

――この景色は。この世界は。

赤いインクを落とした水槽越しに見る景色のように、重くて、遠くて、揺らめいていて。

いったん気になりだすと、気になってしようがなくなってしまう。

見るほどに、赤は濃くなり、視界を遮る。

わたしの邪魔をするように、赤は世界を遠くへ沈めていく。

それでも見ようと瞳を凝らすと――

今までにない鮮烈な赤が目の奥を焼いた。

駄目――
警告されているような気がした。
駄目――
呼ばれている。誰かに呼ばれている。
駄目――
ここにいてはいけないんだ。
向こうに行ってはいけないんだ。
するとわたしは戻るほかなく――

生词表

———————————-
それは物語の予兆
———————————-
物語 ものがたり 故事
予兆 よちょう
木 き 木々 きぎ
伸びる のびる 【自】 延伸
放題 ほうだい 自由地
枝  えだ
伸ばす のばす 【他】
覆い おおい <名>
隠す かくす 覆い隠す おおいかくす
深い ふかい
林 はやし 森 もり
幹 かん 樹干
古木 こぼく
立ち並ぶ たちならぶ 并排
たけ =物の高さ
草 くさ
生い茂る おいしげる 繁茂
狭い せまい
走る はしる 【五】 跑
振り返る ふりかえる 回頭看
瓦 かわら
並ぶ ならぶ 【自】
屋根 やね 屋頂
時代劇 じだいげき 歷史劇
立派 りっぱ
構え かまえ 構造
門 かど
見事 みごと <形動> 好看
屋敷 やしき 宅邸
平屋 ひらや 平房
家屋 かおく
蔵 くら・ぞう
由緒 ゆいしょ 來由
旧家 きゅうか 歷史悠久的家系
涼しい すずしい
げな =~らしい・ようだ
鈴 すず
向き直る むきなおる (なになに)と向き直る 朝著xx方向過去
ぐっと 使勁地
引かれる ひかれる =引かされる 「引く」 被拉著
更に さらに
速める はやめる 加速
勾配 こうばい 斜坡
急に きゅうに
山道 さんどう・やまみち
登る のぼる
舗装 ほそう
外れる はずれる =離れる(はなれる)
分ける わける 【他】 分かれる わかれる 【自】
進む すすむ
追いかける おいかける
急いで いそいで <副> 急ぐ いそぐ
足元 あしもと 腳下
踏みしめる ふみしめる 用力踩
視界 しかい 視界が開(ひら)ける
開ける ひらける 【自】 開闊  開ける あける 【他】 打開
恐らく おそらく 恐怕
中腹 ちゅうふく
辺り あたり 附近
見上げる みあげる 仰視
数 すう
年月 ねんげつ
雨風 あめかぜ
共に ともに ~とともに
過ごす すごす
趣 おもむき 韻味
根 ね
下ろす おろす 根を下ろす 扎根
他 ほか
通り過ぎる とおりすぎる 越過
ゆらり 輕輕搖動 ゆらりと~
揺れ ゆれ
見覚え みおぼえ 仿佛見過
もぐ もがれる
花びら はなびら 花瓣
ひらひら 飄揚 ひらひらと舞う
奇妙 きみょう
既視感 きしかん
喪失感 そうしつかん
感覚 かんかく
インク ink
落とす おとす
水槽 すいそう
越し ごし ~越しに見る 例:眼鏡越しに見る
重い おもい
遠い とおい
揺らめく ゆらめく
濃い こい
遮る さえぎる
邪魔 じゃま 妨礙
沈める しずめる 【他】 把~沉沒
瞳 ひとみ
凝らす こらす 集中
鮮烈 せんれつ 強烈
奥 おく 深处、盡頭
駄目 だめ
警告 けいこく
呼ばれる よばれる 呼ぶ
向こう むこう 對面

2,663 次浏览 | 没有评论
2010年8月18日 | 归档于 红线

刺身和寿司

今天第一次吃生鱼片,确切的说法是刺身。不单是海鱼的肉能做刺身,虾、鱿鱼、章鱼、马肉、牛肉、牛肝等都能做刺身
手机拍的照片,效果只能将就了

武藏日本料理店

牛肝刺身(差点还点了盘马肉刺身)。一开始感觉很恐怖,但实际上味觉并不像想象中一样,沾了清油和盐,基本没有腥味,咀嚼起来既脆又柔软(有点像木耳),不过余还是咀嚼了几口赶紧吞了。只吃了一片

海鲜刺身拼盘。柠檬片左边是山葵泥,上边的就是金枪鱼,往右边过来是章鱼。远处是海鲜酱油,酱油的味道很鲜。尝了一片章鱼和一片金枪鱼。章鱼还好,有点像牛肉干(汗),金枪鱼咬了半口差点就吐出来,很肥厚,门牙切下去的触感不能形容,剩下的是用紫苏叶包住,夹带萝卜丝,才吞了下去

卷寿司、握寿司和鱼子寿司。其实还有一盘卷寿司,18个,没有拍摄。中间夹黄瓜的没什么味道,吃夹金枪鱼的寿司由于沾的山葵泥太多,辛辣味一下子冲上鼻子。寿司因为带有米饭,咀嚼生鱼肉的心理感觉没那么强烈。握寿司就只吃了个半熟的虾肉寿司和章鱼肉寿司。一盘寿司中最贵的就是那个鱼子寿司了,尝了几颗鱼子,蹦蹦的很有趣,化了之后感觉很像油,来整个余怕是会受不了。姜片很好吃(汗

饮的冷酒(也即是冰镇的清酒)是沈阳产的,很淡,一杯干完没什么问题,听说味道和日本的还是有差异。之后又来了个排骨盖饭定食,差点被撑死。这里的盖饭比吉野家的要实惠多了。

出来看到很有特色的一栋建筑,听说是日治时期的

之后去火车站附近的商场逛了一圈。卖海鲜的摊档。

2,952 次浏览 | 没有评论
2010年8月7日 | 归档于 私语