
2008年7月31日
搜索是你明确地知道自己要找什么东西时所做的事情,而发现是你并不明确地知道一些好东西存于世上的时候,这些东西主动找到了你。
以“发现”为主要功能的网站正如雨后春笋般在互联网上崭露头角。许多公司推出了能够帮助消费者找到新网站、新唱片或者新朋友的推荐功能——不管想要什么,去互联网上都可以发现那些你闻所未闻却又注定会一见钟情的东西。
这种功能并不是互联网时代的专利。早在互联网出现之前,在那些用钢筋混凝土搭建起来的小店面或者大卖场里,个体业主们就已经在靠自己敏锐的目光和三寸不烂之舌,根据你的个性特点把货架上的商品源源不断地放进你的购物篮。
走进任何一条商业街上的某间服饰专卖店,流露出一点购物的倾向,导购人员就会走到跟前,指着不远处说:“那款是最近刚上架的新品,看起来与你的气质比较搭配”。这时你很可能会继续和导购人员交谈,多透露一些自己关于衣着打扮的看法,让他或她继续给出建议,帮助选中最心满意足的款式。
这就是我们所说的“逛”街。在走出家门的时候,你的脑中或许有买衣服的计划,却并不知道应该买哪种品牌、款式、颜色、布料、价位的商品。走进商店之后,顾客面对未知的货架开始了自己的“发现”之旅,而商店向顾客提供的“推荐”机制随即开始运作——虽然在多数情况下,你并不会意识到它的存在。
我们不仅仅是在买商品,我们还和商品紧紧地捆绑在一起。我们和它们之间存在着千丝万缕的联系。看看架上收藏的无数影碟、iTunes播放器里的曲目列表、躺在橱柜里那些用过的手机,从每一样东西身上都可以看出来——你是怎样的一个人,或者说,你想成为怎样的一个人。
每个人都通过所购买的东西把内在的自己呈现出来。从另外一个角度说,人们每天都把个性穿在、戴在、挂在身上,刻意或无意、直白或含蓄地告诉外界:“喏,这就是我。”
在真实的世界中,我们通过外表所蕴含的信息以及所处的环境和既有的经验对他人做出判断。这种直觉通常情况下十分管用而且立竿见影,但是有时候它却会失算,毕竟感觉在很多时候是说不准的。但是如果换到互联网上,这件事情就会变得容易和清晰得多。
“发现”在网上的兴起
日复一日,消费者都会在互联网上以数字化的形态留下自己的心得体验。比如豆瓣网上有一半观众觉得《满城尽带黄金甲》看着“还行”,两成观众打分“力荐”;比如大众点评网的用户对北京川办餐厅的打分是:口味26、环境13、服务12;又比如人们在网上对各张专辑做出的评论、贴上的标签。每当用户留下这样的标记,除了能够让其他人知道这些东西看起来如何、听起来怎样,还在不知不觉地向外界描述自己的口味。
对于那些可以获得、储存并且分析这些信息的公司而言,商机大得惊人。这些公司比传统商店里的导购人员更了解你,更进一步说,指不定比你的闺中密友更清楚你的兴趣所在。这些公司可以精准地描绘你的口味——在专家的帮助下,他们可能比你还更容易说清你自己属于哪类人群——以此判定你最可能会在现有的产品选项中买下什么。这些在过去听起来像“第六感”的东西,现在正以1和0为载体被保存在网络服务器上。
对于那些以推荐功能为核心的网站来说,他们再清楚不过地知道,互联网正在从搜索的时代进入发现的时代。两者的区别在哪里呢?搜索是你明确地知道自己要找什么东西时所做的事情,而发现是你并不明确地知道一些好东西存于世上的时候,这些东西主动找到了你。
在搜索领域,胜负已经非常明显——在国外,Google遥遥领先,在国内,百度一枝独秀。但是,起码到目前为止,还没有哪个推荐引擎是当之无愧的市场领导者。
建造个性化的发现机制需要对目前网上所有的表述、分类及评价方式进行深入研究,这绝非易事。但是如果有公司可以把这样的东西做到手机上,那么,这样的发现工具不仅仅可以改变营销,甚至整个商业社会。“推荐系统将会成为接下来十年中最重要的革新之一。”曾经在90年代中期开发出第一批推荐引擎的米尼苏达大学计算机科学系教授约翰·里德(John Riedl) 说过,“社会化网络将会被这些系统所驱动。”
亚马逊网络商店(Amazon.com) 很早就意识到了一个优秀的推荐系统可以带来什么,直到现在依然是同类网站效仿的对象。亚马逊通过数据挖掘算法和比较机制将用户的消费偏好与其他用户进行对比,借以预测用户可能感兴趣的商品。
这样的推荐系统建立在对商品深入了解的基础之上。看看亚马逊网络书店上的书籍和影音制品,即便是再偏门的品种,他们也被管理员赋予了多种类型(Genre)和关键词(Keyword),而且用户还可以为它贴上自己中意的标签(Tag)。
但是亚马逊“相关图书”和“相关唱片”的推荐机制依然没有做到足够的个性化,它看起来更像是一个固执己见的推销员,而非熟识并且值得信赖的小店老板。亚马逊的系统倾向于向用户推荐那些显而易见的的相关产品,却对用户缺乏更深入的了解,并因人而异地推荐商品。新一代的发现型网站正在弥补这方面的缺失,搞清楚用户为什么选择了某些商品,为用户也贴上复杂的属性标签。
为了建造更好的推荐系统,美国最大的DVD租赁网站Netflix设置了100万美元的奖金,准备奖励给可以让推荐机制性能提高10%的人。在这个竞赛公布之前,Netflix负责推荐系统的副总裁吉姆·贝内特 (Jim Bennett) 曾经怀疑是否能够有人在十年之内完成这个目标,但他也坚持,这个目标确实值得公司付出100万美元。但是在五周之后,已经有37人提交了改进方案,其中有两名选手成绩离获奖标准并不遥远。
两种“发现”
而电影推荐网站“What to Rent” (意为“租点什么”) 已经在发现机制的完善之路上迈出了更大的步伐。该网站的两位创始人马修·库尔克 (Matthew Kuhlke) 和亚当·盖特吉 (Adam Geitgey) 对电影极为痴迷,他们俩用自己在电影方面的知识和分类海量电影的经验来武装自己的电影数据库。
而同时,他们让初次光临的用户回答一系列随机的问题,有的甚至和电影看起来关系不大,比如说:“你最喜好哪一类体验——精神、智力还是身体上的?”或者“给你多少钱才能让你在下半辈子里每天都穿一些滑稽古怪的装束?”他们通过心理测试来了解用户,以推荐合适的电影。“看一部电影的时候,你就与这部电影产生了互动。这种互动和你与人打交道并没有多少区别。” 库尔克说,“你都是在建立一种关系。”
“工欲善其事,必先利其器”。换到发现型网站上,那就是想要推荐给用户最有效的结果,那么就必须先了解自己的用户。如何了解自己的用户,成为此类网站首要解决的问题。
显然,“What to Rent”网站两位创始人的敏锐目光给他们帮上了大忙。在网站上,他们用许多问题来了解用户的特质,并且推荐DVD。在现实生活中,他们会去公司附近的饭店里 挑战一些更有难度的推荐方式。他们随机地挑选一些人,不和他们进行交谈,只是根据对行为举止的观察来推测他们最喜欢的某部电影。
他们会在人群中选中端水送饭兼收盘子的伙计,然后开始描述这个观察对象:“破烂的牛仔裤、金属手镯、各部位有若干纹身”,“20岁以上接近30岁”,“在一个年轻化的餐馆里工作”,“在这里的年轻人很多没有念完高中,因为他们很难集中注意力,但是却花了大部分时间用来阅读一些由艺人兼作家——比如尼克·凯夫 (Nick Cave) ——撰写的哲理书籍”。由此,库尔克认为《星河战队》(Starship Troopers) 这样有点轻微反政府情节又有点刺激动作场面的电影最适合他的口味。果然,他们从对方嘴里得到这个答案。
这种了解内容和用户的特质,送上合适产品的做法,被称为“基于内容的过滤” (Content Based Filtering) 。正如“What to Rent”需要在事前对用户做一个问卷调查,他们同样要对内容产品贴上各种门类的标签并且写入数据库。?荩
在这类发现型网站中,国内用户最熟悉的是音乐推送网站“潘多拉” (Pandora) 。只要在“潘多拉”网站首页的播放器中输入用户最喜欢的歌手名字或者歌曲标题,网站就会自动建立一个网络电台,源源不断地播放最符合用户口味的曲目。用户喜欢或者不喜欢一首歌,可以点击相应的反馈按钮,让系统更明白自己的喜好。
这种有趣的体验建立在惊人的“音乐基因工程”(Music Genome Project)基础之上。45位音乐分析师——其中大部分拥有音乐方面的学位——以每月15000首歌的速度完善“潘多拉”的曲目数据库,每首歌都拥有400字左右的详细介绍。在播放每一首歌的时候,点击“为什么选择这首歌”的选项,就可以得知目前播放的曲目与用户输入曲目的所有相似之处。
而随着社会化网络(Social Network) 的大规模兴起,另一种被称为“协同过滤”(Collaborative Filtering) 的推荐方式也得到了越来越广泛的应用。如果说“基于内容的过滤”可以被视作由机器完成的推荐,那么“协同过滤”就可以被看成由人完成的推荐。
以“协同过滤”推荐机制为核心,网站并不需要做出如同“What to Rent”的“性格—产品”匹配,而只需要了解“用户都在同哪些人进行交往”、“用户加入了哪些组群”、“用户都在看谁的页面”这样的问题,然后根据“物以类聚、人以群分”的思路为用户推荐他的伙伴都感兴趣的事物。
国内以图书推荐起家的“豆瓣”就是这方面的代表。从建站以来,豆瓣就只是提供给用户关于图书、电影和音乐的基本信息,而不需要由网站员工对用户或者产品做出“贴标签”式的逐一了解。用户在“豆瓣”上发现新的产品,完全以他们参与的小组和关注的对象作为切入点,在网站上的参与度越高,发现的效果也就越好。
无论从用户还是服务提供者的角度出发,这两种推荐方式都有各自明显的优劣。如果采用“基于内容的过滤”,那么在完成内容和用户互相匹配的过程中,就可能出现“越读越窄”、“越听越窄”、“越看越窄”的问题,提供的内容完全与用户兴趣点相吻合,而没有发散。
“协同过滤”在很大程度上避免了这个问题,但是他需要用户达到一定数量级之后才能发挥出网站创建者预想的效果,这种“网络效应”使得同一市场上的后来者很难找到切入的机会。而用户一旦不能得到“好处”,可能迅速离去,而来不及提“利他”的后话。■
转载:http://blog.csdn.net/java060515/archive/2007/04/18/1568658.aspx
posted @
2008-07-31 16:07 流浪汗 阅读(426) |
评论 (0) |
编辑 收藏
推荐机制的两种方式:1、基于用户行为;2、基于产品基因;推荐机制建立的前提:1、用户行为数据记录;2、产品特性基因构成;
1、基于用户行为:案例amazon(亚马逊)
亚马逊的推荐系统是程序自动化和用户相关购物信息天才集合的经典之作。亚马逊花了十几年的时间建立和完善了这个系统。这个系统基于产品和相关用户的巨大数据库;记录的信息包括你在过去几年或几分钟内做过什么。
所有推荐都基于用户个人行为,加上商品本身,或者是其他用户在亚马逊的活动。不管是因为你以前购买过相关产品,还是因为其他很多用户都喜欢,亚马逊每推荐给你一件商品,都增大你把它放进你的购物筐的可能。
2、基于产品基因:案例pandora(潘多拉)
音乐染色体组项目的 推出,目的在于把音乐解析成为最基本的基因组成。它的基本想法是:我们因为音乐的某些特性喜欢音乐--那么为什么不能根据音乐的某些相似之处设计出一套推 荐系统呢?这类推荐系统应该属于基于产品的推荐。但具有深刻创新意义的是,产品(音乐产品)的相似性,通过“基因”组成来衡量。
这种“即刻满足感”是很难抗拒的。因为pandora 了解音乐相似性背后的因素,它不需要了解用户的好恶,就可以把用户黏住。确实,pandora 需要把握用户的口味或记忆--但这正是蕴藏在音乐本身的dna中了。当然pandora有时并不完美,会播放不对用户口味的音乐。但这很少发生。
拓宽联想:音乐产品的基因组适用于其他产品吗?也就是说:标签(tag)是否就能代表产品的基因呢?案例del.icio.us
社会化书签网站中首屈一指的 del.icio.us (见译言有关del.icio.us的译文)则采用不同的办法--让用户自己来鉴定、标识。这种自组织的方式相当成功,del.icio.us 很快在最初的使用者中流行起来。今天,del.icio.us 已经不仅仅是一个书签网站--它还是一个新闻网站和搜索引擎。但del.icio.us是否也是一个推荐系统呢? 答案是肯定的。这是一个非常简明的推荐系统,它指基于一个基因--那就是一个标签。
思考:每一个网站都有自己的一套为用户创造体验与价值的方式,而真正的“即可满足感”又有几家能做到。尤其现在的web2.0类型网站都首先标榜“用户体验”,在copy一个成功模式之后,以为就同样完整的copy了相同的“用户体验”的自我感觉其实是错误的。比如说:我们很多的digg类、书签类新网站,并没有发挥自己的创造力,甚至根本都没有结合国情。或许未来下一个更好的系统是完美地将用户行为与产品基因相结合的推荐模式,不管是商务网站的产品推销,还是社会化网站的服务,因为:
一个出色的推荐机制不光对netflix,对其他网络企业也非常重要。这是因为用户的网上活动分为两类:搜索和浏览。当消费者明确知道她想要什么的时候,她搜索。但当她不太清楚想要什么的时候,她浏览。浏览活动为推荐系统带来了绝好的机会。因为当用户没有集中注意力在找她想要的东西时,她对外来的建议是敞开的。
在浏览过程中,用户的注意力(和他们的钱),都等着你去抓住。通过向用户展示有吸引力的东西,网站可以使交易成功的可能最大化。所以如果网站能增加给用户提供好推荐的几率,就能赚更多钱。显然这不是一个容易解决的问题,但解决这个问题带来的好处是巨大的。
转载:
http://blog.csdn.net/java060515/archive/2007/04/18/1568789.aspx
posted @
2008-07-31 16:05 流浪汗 阅读(419) |
评论 (0) |
编辑 收藏
网站推荐机制是电子商务或内容网站的核心功能之一。例如你在一个网站买了一本书后,网站会推荐其他你可能会感兴趣的书。这被认为是亚马逊等电子商务巨头成功的关键。本文对几个出色的推荐系统进行了较透彻的分析。
2006年10月,Netflix搞了一次不寻常的有奖竞赛。这家网上DVD租赁公司开出奖金一百万美元,奖励给能把他们网站的产品推荐机制提高10%的人。Netflix 以富有创新精神和闯劲著称。而一百万美元的奖金对于这样一家公司来说,其实并不象听起来那么多。
有奖竞赛还在进行当中(“至少要进行到2011年10月2日”)。所以这是一场炒作活动还是希望花小钱进行研究,我们还不得而知。而对Netflix来说,更好的推荐机制是必不可少还是锦上添花?今天,Netflix正面对从沉睡中醒来的巨人BlockBuster的挑战,因此它必然要寻找制胜的先机。出色的推荐机制会留住老用户,吸引新用户。比如当一个用户还DVD时,系统会推荐给他/她可能会喜欢的另一部电影。这就增加了这个用户回来再租DVD的可能。
浏览与推荐
一个出色的推荐机制不光对Netflix,对其他网络企业也非常重要。这是因为用户的网上活动分为两类:搜索和浏览。当消费者明确知道她想要什么的时候,她搜索。但当她不太清楚想要什么的时候,她浏览。浏览活动为推荐系统带来了绝好的机会。因为当用户没有集中注意力在找她想要的东西时,她对外来的建议是敞开的。
在浏览过程中,用户的注意力(和他们的钱),都等着你去抓住。通过向用户展示有吸引力的东西,网站可以使交易成功的可能最大化。所以如果网站能增加给用户提供好推荐的几率,就能赚更多钱。显然这不是一个容易解决的问题,但解决这个问题带来的好处是巨大的。推荐的几种方式如下:
- 个性化的推荐--根据用户过去在网站的行为进行推荐
- 社会化推荐--根据类似用户过去在网站的行为进行推荐
- 基于产品的推荐--基于产品本身的特性进行推荐
- 以上三者的混合
我们现在通过实例详述上述方式。包括老牌网站如亚马逊 Amazon,以及新秀如Pandora(译者:一个收听网上音乐的站点)和del.icio.us(译者:著名的社会化书签网站)。
亚马逊--推荐之王
亚马逊被广泛认为是网上购物行业的领袖,特别是推荐机制的使用。过去十几年间,该公司投入了大量金钱和脑力开发推荐机制,来促使用户更多地购物--包括对你浏览历史、购买历史,以及其他用户购买数据的分析。让我们看看亚马逊推荐机制的几个方面。下面是登陆到我的亚马逊帐户后网页显示的主要部分:

(译者:上图抬头是“消费者浏览了这件商品后买了什么?”下面是三件商品:52%的消费者买了你正在看的这套衬衣;19%买了另一种款式;5%买了灰色的那种)
这部分是社会化推荐。注意,非常量化的分析,给我一个基于数据统计的理由为什么我应该买这套衬衣。而同时这又是个性化的推荐,因为它基于我刚刚点击查看的产品。

(译者:上图抬头是“为你准备的新货”。下面是几本书以及链接“为什么我们推荐这本书给你?”)
这一部分是“基于产品的推荐”,具体说是新书推荐。点击“为什么我们推荐这本书给你?”的链接会显示我的购买历史。因此这同时也是个性化推荐--基于我过去行为的推荐。
这一页上有其他四个部分采用上述推荐机制的结合。我们用下表概括:
亚马逊个性化推荐系统
| |
| 你的浏览历史 |
你的购买历史 |
| 实际浏览商品 |
新产品(基于产品的推荐) |
| 相关商品(基于产品的推荐) |
相关商品(基于产品的推荐) |
| 别人购买的(社会化推荐) |
别人购买的(社会化推荐) |
很自然,这个系统是对称和覆盖全面的。所有推荐都基于用户个人行为,加上商品本身,或者是其他用户在亚马逊的活动。不管是因为你以前购买过相关产品,还是因为其他很多用户都喜欢,亚马逊每推荐给你一件商品,都增大你把它放进你的购物筐的可能。
超越亚马逊
亚马逊的推荐系统是程序自动化和用户相关购物信息天才集合的经典之作。亚马逊花了十几年的时间建立和完善了这个系统。这个系统基于产品和相关用户的巨大数据库;记录的信息包括你在过去几年或几分钟内做过什么。其他新公司如何与之竞争呢?
出乎意料的是,有办法竞争。答案来自与网络购物没什么关系的学科--遗传学。如你所知,遗传学研究DNA片段--基因,如何承载影响人类特性和行为的密码。比如一家人因为他们有某些基因是相同的,因此长相和行为有类似之处。遗传学是一门有150年历史的学科,在医学和历史学的研究中都是一个重要工具。但遗传学一个出乎意料的应用于2000年1月6日出现--Time Westergren和他的朋友们决定把遗传学的概念引入到音乐中。
潘朵拉(Pandora)--基于遗传学的推荐系统
音乐染色体组项目的推出,目的在于把音乐解析成为最基本的基因组成。它的基本想法是:我们因为音乐的某些特性喜欢音乐--那么为什么不能根据音乐的某些相似之处设计出一套推荐系统呢?这类推荐系统应该属于基于产品的推荐。但具有深刻创新意义的是,产品(音乐产品)的相似性,通过“基因”组成来衡量。

(译者:上图是Pandora音乐播放器向用户解释为什么播放一首歌曲:“基于你告诉我们的信息,我们播放这段音乐。因为这段音乐微妙地使用人声合声,混合了声学和电子乐器,有以人声为核心的审美特点,音调以大调为主,以及其他音乐基因组项目中发现的相似性。”)
在奋斗了几年,分析了海量的音乐后,这个项目积累了足够的数据,并推出了名为Pandora的在网上收听音乐的服务。Pandora 由于准确的推荐和用户低成本进入(译者:指用户可以很容易地就试用Pandora的服务)取得巨大成功。用户只需选一名歌手,或一首歌,就可以建立一个用户自己的电台,播放类似的音乐。
这种“即刻满足感”是很难抗拒的。因为Pandora 了解音乐相似性背后的因素,它不需要了解用户的好恶,就可以把用户黏住。确实,Pandora 需要把握用户的口味或记忆--但这正是蕴藏在音乐本身的DNA中了。当然Pandora有时并不完美,会播放不对用户口味的音乐。但这很少发生。
很自然地我们会问:这个基于基因的推荐方式能不能应用于其他产品--如书籍、电影、酒、餐馆、或旅游点呢?对于每一类产品,它的基因又由什么因素组成呢?比如说,对葡萄酒来说,它的基因是否包括对酒的口味的描述呢:黑莓口味、土味、果香型、复合型、混合型,等等。而书籍的基因是否是描述故事情节的用语呢?如果基因真的是让某一个物体在我们头脑中产生独特形象的因素,对于不同的事物我们应该可以把他们的基因定义出来。其实在过去的几年间,我们已经在互联网上做这样的事了。这就是“标签(tagging)”。
Del.icio.us -标签会成为基因么?
Pandora 的初始投入是巨大的。因为成千上万的音乐需要人工来鉴定。社会化书签网站中首屈一指的 del.icio.us 则采用不同的办法--让用户自己来鉴定、标识。这种自组织的方式相当成功,del.icio.us 很快在最初的使用者中流行起来。今天,del.icio.us 已经不仅仅是一个书签网站--它还是一个新闻网站和搜索引擎。但del.icio.us是否也是一个推荐系统呢?

(译者:上图显示加了“Linux”标签的最受欢迎的网页链接,右侧是相关标签。)
答案是肯定的。这是一个非常简明的推荐系统,它指基于一个基因--那就是一个标签。例如在上图中,我们看到最受欢迎的加了“Linux”标签的网页链接,同时还看到相关链接如“Open source (开源软件)”和“ubuntu (Linux 操作系统的一个版本)”。而一个更令人兴奋的推荐系统是基于多个标签的吻合。然而由于算法还不完善,这一点的作用并不明显。但至少,这一功能对于Read/WriteWeb 的网页(译者:原文的站点)是有效的,在下图中,可以看到右侧显示的与 Read/WriteWeb 类似的其他博客。

因此,del.icio.us 的机制有可能发展成为一个有趣的自助分类、组织的推荐系统。如果有足够的用户并逐步调整系统,用群体的力量加标签的方式可以建立起一个对书、葡萄酒、音乐同样有效的推荐系统。前题是,标签要准确到能够成为被标签对象的基因!
结论
推荐引擎是网上电子商务系统和用户体验的一个重要组成部分。网上零售商们有强烈的意愿向那些正在浏览的用户提供推荐,以驱使他们购买。亚马逊有一套非常出色的个性化推荐系统,是这个领域的领先者。其他网上零售商面临的问题是缺乏用户信息和软件构架不完善。
最新的推荐引擎,如受遗传学启发的Pandora和基于群体社会化标签的del.icio.us都是非常有趣的新发展。这些系统的长处在于“即刻满意度”--不需要用户提供他们的偏好和过去的浏览或消费历史。不管未来如何发展,亚马逊、Pandora和del.icio.us目前展示了极其出色的推荐技术。我们向您推荐这几个系统,并对未来拭目以待。
转载:http://blog.csdn.net/java060515/archive/2007/04/18/1568824.aspx
posted @
2008-07-31 16:03 流浪汗 阅读(318) |
评论 (0) |
编辑 收藏
在在线数字音乐领域中,如何能够真正深入挖掘长尾的价值,采用主动的音乐推荐模式,去帮助用户找到符合自己口味的新音乐,成为一个新的挑战和机会。八宝盒(
www.8box.cn)是一个基于音乐分享及偏好而建立起来的SNS服务, 利用多年积累研发的推荐引擎,8box能帮你找到可能喜欢的音乐,帮你分析出哪些用户是你的音乐“同好”。 八宝盒通过你推荐、试听、打分、评论音乐的过程,学习你的口味,并依此帮助过滤出你喜欢的音乐。发现音乐的最好途径是通过你的同好。八宝盒能够根据你的口味,推荐相似的用户给你。应该说“个性化推荐引擎--IntelliProvider”是音乐八宝盒的技术基石。
简单的来说,8box推荐音乐的依据有三种:
1.依靠音乐本身的属性信息判断音乐的相关性,做出推荐。
2.依靠听众对这个音乐的评价和反映来推断音乐的相关性,做出推荐。
3.依靠分析用户的行为,发掘出用户的音乐同好,从而做出推荐。
第一种依靠音乐本身的属性,强调音乐本身的信息来判定音乐。在国内的音乐网站来说,音乐信息的判断基本上停留在对音乐资料的搜集上,比如一首歌的演唱者、所属专辑、这首歌的音乐类型等等。8box建立了庞大的音乐信息库,百万张唱片的音乐信息可以增加音乐推荐的准确度。
一个简单的例子:喜欢听朴树的《生如夏花》的人,可以推荐给他《傲慢的上尉》。
但仅仅这样还是不足够的,音乐间的关联不仅仅依靠本身的信息就能完全判定的。喜欢听朴树的用户不仅仅爱听《生如夏花》和《傲慢的上尉》,还可能喜欢听许巍的《蓝莲花》,喜欢听《蓝莲花》的用户,还可能喜欢听郑钧《私奔》。。。而《生如夏花》和《私奔》在音乐本身的信息上是无法直接关联的,所以我们需要更多的途径来丰富完善推荐音乐的依据。
于是8box引入第二种--依靠用户的评价和反映。8box在用户听音乐的时候,搜集用户的行为,引导用户对所听的音乐进行打分,评价,加标签等,分析用户的喜好。喜欢这个音乐的人对哪些音乐还感兴趣?通过8box自主开发的推荐系统,分析出比率,为每个音乐添加了不同的标签。
于是《生如夏花》和《私奔》的关联就顺理成章了。
是不是来自好朋友推荐的东西更可信一点?因为有了对朋友的了解,朋友间的共性决定了这样的推荐更可能是你喜欢的。基于这个原理,八宝盒不仅搜集音乐的相关信息,也搜集了用户的信息,分析每个用户之间的相似度,并向用户推荐相似度高的同好用户。随着用户在网站的操作越多,对用户的信息搜集越全面,用户越可能找到同好。依据同好的推荐,可以发现更深一步的音乐关联,找到更多你喜欢但是没有听到过的音乐。同时也能找到更多志同道合的朋友。
与其他的分享社区一样,用户可以将其他用户加为好友,创建歌迷小组(在八宝盒中叫饭团-fans团)。在饭团里交流信息,推荐音乐,或者上传自己的音乐收藏。随着团员的增加,饭团的共享空间也会增加。在音乐八宝盒,用户可以创建并分享自己的播放列表,并利用网站提供的播放器欣赏音乐。此外,还借鉴了Pandora的方式,利用音乐导航功能可以从任意一首音乐始连续播放相关的音乐。屏幕右边的浮动播放器不但可以保证页面切换时不间断播放歌曲,用户评分等操作也被集成在播放器中。音乐八宝盒全方位的推荐,可以让用户很快找到大量喜欢的歌曲,八宝盒的个人电台功能,将用户喜欢的歌曲自动作为电台不间断的播放。每天24小时都听自己喜欢的,不是件难事儿了。大量Ajax的使用,使得很多操作变得简易,大部分都能一步完成,而提示也很温情。这个网站还与blog紧密结合了。当一个歌曲被建立的时候,同时会搜索到msn space上所有与这首歌相关的blog文章,然后列在歌曲的下方。当然,你也可以把喜欢的歌曲以漂亮列表的形式加到自己的blog上,而你要做的仅仅是复制粘贴八宝盒提供的一小段代码。
通过强大的推荐技术,结合网站的社会性元素,八宝盒的主动的音乐推荐模式将帮助用户找到属于自己的”音乐长尾”。
转载:
http://blog.csdn.net/java060515/archive/2007/04/20/1571890
posted @
2008-07-31 14:51 流浪汗 阅读(501) |
评论 (0) |
编辑 收藏
如果说过去的十年是搜索技术大行其道的十年,那么个性化推荐技术将成为未来十年中最重要的革新之一。目前几乎所有大型的电子商务系统,如Amazon、CDNOW、Netflix等,都不同程度地使用了各种形式的推荐系统。而近来以“发现”为核心的网站正开始在互联网上崭露头角,比如侧重于音乐推荐的八宝盒,侧重于图书推荐的豆瓣等等。
那么,一个好的推荐系统需要满足什么目标呢?
个性化推荐系统必须能够基于用户之前的口味和喜好提供相关的精确的推荐,而且这种口味和喜欢的收集必须尽量少的需要用户的劳动。推荐的结果必须能够实时计算,这样才能够在用户离开网站前之前获得推荐的内容,并且及时的对推荐结果作出反馈。实时性也是推荐系统与通常的数据挖掘技术显著不同的一个特点。
一个完整的推荐系统由三部分构成:行为记录模块、模型分析模块和推荐模块。行为记录模块负责记录能够体现用户喜好的行为,比如购买、下载、评分等。这部分看起来简单,其实需要非常仔细的设计。比如说购买和评分这两种行为表达潜在的喜好程度就不尽相同完善的行为记录需要能够综合多种不同的用户行为,处理不同行为的累加。模型分析模块的功能则实现了对用户行为记录的分析,采用不同算法建立起模型描述用户的喜好信息。最后,通过推荐模块,实时的从内容集筛选出目标用户可能会感兴趣的内容推荐给用户。因此,除了推荐系统本身,为了实现推荐,还需要一个可供推荐的内容集。比如,对于音乐推荐系统来说,一个音乐库就是这样的内容集。我们对内容集本身需要提供的信息要求非常低,在经典的协同过滤算法下,内容集甚至只需要提供ID就足够。而对于基于内容的推荐系统来说,由于往往需要对内容进行特征抽取和索引,我们就会需要提供更多的领域知识和内容属性。这种情况下,还是拿音乐举例,歌手、流派之类的属性和音频信息就成为必需的内容集信息。
迄今为止在个性化推荐系统中,协同过滤(Collaborative Filtering)技术是应用最成功的技术。目前国内外互联网上有许多大型网站已经应用这项技术为用户更加智能的推荐内容。如果你想要研究协同过滤,一定不能错过MovieLens(
http://movielens.umn.edu/)。它是协同过滤最著名的研究项目之一。
第一代的协同过滤技术,又被称为基于用户(User-based)的协同过滤。基于用户的协同过滤,基本原理是基于用户行为选择的相关性。用户的行为选择这里指的是下载、购买、评价等等能够显式或者隐式体现出用户喜好的行为。在一个典型的基于协同过滤技术的推荐系统中,输入数据通常可以表述为一个m×n 的用户内容矩阵R,m是用户数,n是内容数。矩阵的值与内容的类型有关,通常由行为记录模块决定。如果内容是网上书店中的书,则矩阵的值可以表示用户购买与否,例如1表示购买,0表示没有购买;或者表示用户对它的评价有多高,这样的评价值就可以有几个等级,比如常见的1~5级评价制。
基于用户的协同过滤,通过比较目标用户的一系列行为选择和其他用户之间的相似性,来识别出一组相互具有类似喜好的用户,又可以称为“同好”。一旦系统能够识别一个用户的同好用户,就能够将他们最感兴趣的内容作为当前用户的推荐结果推荐给这个用户。也就是说,以前的行为选择与你相似的用户,在以后的行为中很可能也会和你相似。因此将这些用户做为基准来向你推荐内容。
协同过滤的核心问题是寻找与目标用户兴趣相近的一组用户。这种相似用户通常被称为最近邻居(Nearest Neighbor)。用户之间的相似度是通过比较两个用户的行为选择矢量得到的。目前,比较行为选择矢量的相似度计算方法有许多种,比较经典的算法包括泊松相关系数(Person Correlation Coefficient)和余弦相似性(Cosine-based Similarity)。
“最近邻居”产生后,我们就能够计算得到用户最可能感兴趣的内容集(也叫做TopN推荐集)。为了得到推荐集,分别统计“最近邻居”中的用户对不同内容的兴趣度,取其中排在最前面的内容作为推荐集。下面是一个简化的示例:假如用户张三有两个同好:李四和王五。
张三喜欢看电影A;
李四喜欢看电影A,B,C和D;
王五喜欢看电影A,B,D,E和F;
这样,推荐系统就能够过滤出相似用户都喜欢的电影B和D作为张三最可能也会喜欢的电影推荐给张三。
基于用户的协同过滤技术在个性化推荐系统中获得了极大的成功,但它有自身的局限性。推荐集的产生方式意味着一个内容只有已经被用户选择(购买)后才有机会被推荐给其他用户。对于一个网上书店来说,新上架的书因为还没有被相当数量的用户购买或者评价的记录,便很少有机会被用户的“最近邻居”筛选进入推荐集。这个问题,也被称之为协同过滤的“冷启动”问题。
此外,因为计算用户的相似度时,是通过将目标用户的历史行为记录与其他每一个用户的记录相比较得出的,所以对于一个现实的推荐系统来说,扩展性将成为非常严重的问题。设想一下,对于一个拥有上百万用户的网站来说,每计算一个用户都将涉及到上百万次的比较,更不要说其中会带来的大量数据库IO操作的开销。
于是第二代基于内容项(Item-based)的协同过滤技术就产生了。与基于用户的技术不同的是,这种方法比较的是内容项与内容项之间的相似度。Item-based 方法同样需要进行三个步骤获得推荐:1)得到内容项(Item)的历史评分数据;2)针对内容项进行内容项之间的相似度计算,找到目标内容项的“最近邻居”;3)产生推荐。这里内容项之间的相似度是通过比较两个内容项上的用户行为选择矢量得到的。举个例子,假设用户和内容项如下:
|
|
电影A
|
电影B
|
电影C
|
电影D
|
|
张三
|
喜欢
|
|
|
|
|
李四
|
喜欢
|
喜欢
|
喜欢
|
喜欢
|
|
王五
|
不喜欢
|
|
不喜欢
|
不喜欢
|
|
赵六
|
喜欢
|
喜欢
|
|
喜欢
|
可以看出,电影A与D是最相似的。因为张三喜欢A,所以电影D就可以推荐给张三。
不论是第一代的基于用户方法,还是第二代的基于内容项方法,都不可避免的遇到数据稀疏的问题。在任何一个网站中,用户的评分记录或者购买记录,相对整个可供选择的内容集来说,都是很小的一部分。所以在许多推荐系统中,每个用户涉及的数据量相当有限,在一些大的系统如Amazon中,用户最多不过就评价过上百万本书的1%,造成评估数据相当稀疏。当用户评价过的内容之间找不到交集时,就难以判断用户的口味是否相似,难以找到相似用户集,导致推荐效果大大降低。为了解决用户数据的稀疏问题,最方便的办法就是将用户对没有选择过的内容项的评分设为一个固定的缺省值,例如用户的平均评分。针对如何预测遗漏的评分业内又提出了很多种方法,不过一般来说采用最简单的改进方法就可以有效地提高协同过滤推荐系统的准确度。
另外一方面,即便采用了基于内容项的方法,在数据量巨大的时候,计算复杂度仍然成为性能瓶颈。为了进一步解决协同过滤技术的扩展性能问题,目前比较有效的办法是在用户评分数据上做一次聚类分析(clustering)。聚类技术首先将具有相似兴趣爱好的用户分配到相同的分类中。聚类产生之后,它或者将“最近邻居”搜索对象限制在最相近的聚类中,根据类中其他用户的评价预测目标用户的评价,或者用聚类的中心作为近似提取推荐结果。由于用户之间的分类相对变化比较小,因此聚类过程往往可以离线进行,而无需实时计算,这样就大大降低了实时推荐的计算压力,提高推荐系统的速度。一般来说,聚类将用户分为多少个类,推荐系统的整体速度就能够提高多少倍。具体选择什么样的聚类算法,又会因应用领域和数据的分布特性而不同。如果聚类算法选择不当,反而会降低推荐的准确性。近年来,推荐系统的算法技术的发展也有了一些新的方向,比如SlopeOne,SVD等方法,就不一一列举了。
在我看来,一个商用推荐系统的尤其关键之处在于对海量用户数据的处理。因为推荐系统是数据优先,数据的积累越多对推荐的精度就越有好处。而当用户的行为数据真正积累到上百万甚至上亿时,如何在合理时间内得出有效的推荐,就是对推荐技术最大的考验。除此之外,一个优秀的推荐系统需要能够结合内容相似与用户行为相。传统的协同过滤方法是忽略内容本身的属性的,这一方面固然是对数据要求少的优点,但另一方面也带来了难以避免的“冷启动”问题。其实,随着标签系统在互联网上的广泛应用,标签本身就不失为是一种很好的内容属性。如何利用也是值得大家探讨的。充分利用到内容本身的属性,将不同的相似性结合起来,这会给基于协同过滤的推荐技术带来新的动力。最后一点,设计良好的推荐技术要能够从用户对推荐内容的反馈中自行调整和学习。因为实际上每个用户对于推荐的内容都有不同的要求,比如有的用户可能偏好比较热门的内容,有的用户更愿意发现冷门的内容。针对不同用户的反馈来不断学习每个用户的特征,才能够避免所采用算法本身先天的偏差,获得较为理想的效果。
posted @
2008-07-31 14:00 流浪汗 阅读(2121) |
评论 (0) |
编辑 收藏

2008年3月26日
转载:
张驰有道 http://www.jinsehupan.com/blog/?p=25
多谢他的介绍。
1、开篇语
2、概述
3、渊源
4、初识Solr
5、Solr的安装
6、Solr分词顺序
7、Solr中文应用的一个实例
8、Solr的检索运算符
[开篇语]按照惯例应该写一篇技术文章了,这次结合Lucene/Solr来分享一下开发经验。
Lucene是一个使用Java语言写的全文检索开发包(API),利用它可以实现强大的检索功能,它的详细介绍大家可以去Google上搜索一下,本文重点放在Solr相关的讨论上。
[概述]目前国内研究Solr的人不多,而且大多是因为项目开发需要。Solr师承Lucene,为Apache基金会下的一个项目,具体的说它还是Lucene下的一个子项目。Solr出身豪门,而且具有自己的技术特点,填补了以往Lucene仅仅作为开发工具包的遗憾,它是一个完完整整地应用。换句话说,它是一个全文检索服务器,它开箱即用,让我们能立马体会到Lucene的强大功能,为Lucene产品化走出了一大步。

Solr分词原理演示界面
[渊源]最初,CNET Networks使用Lucene API来开发了一些应用,并在这个基础上产生了Solr的雏形,后来Apache Software Foundation在Lucene顶级项目的支持下得到了Solr,这已经是2006年1月份的事了。2006年1月17日,Solr正是加入Apache基金会的孵化项目,在整个项目孵化期间,Solr 稳步地积累各种特性并吸引了一个稳定的user群体、developer群体和Committer群体,并于1年之后的17日正式酝酿成熟,在这之前已经成功发布了1.1.0版。目前的稳定版本是1.2,Solr在9月份的2007Apache年会上大放异彩,在今年11月底将来到香港参加2007亚洲开源软件峰会,遗憾的是为什么不来北京:-(
[初识Solr]Solr服务器不同于普通的关系型数据库,不仅仅在于它核心本质的不同(面向结构化和非结构化数据的不同),很大的不同还在于它的体系架构上。Solr服务器一般情况下需要部署于应用服务器/Java容器上(如果是本机通信不涉及RPC可以不使用Java容器,如采用嵌入方式使用Solr),无法独立工作于JVM上。

Solr架构图
Solr服务器可以存储数据并通过索引对其进行快速高效检索。对外提供HTTP/XML和Json API接口,这使得它能够在多语言环境下集成,比如针对它的客户端的开发。Solr目前的客户端面向的有Java、PHP、Python、C#、Json和Ruby等,遗憾的是没有面向C/C++(这也是本人目前在研究的),研究音乐搜索分类的Brian Whitman曾在苹果平台上使用JNI技术在C代码中嵌入Solr实现检索,不过是一个Cocoa工程。有了这些客户端,使用者能很方便地将Solr集成到具体运用中。目前最完善的当属Java客户端Solrj,以及加入到Solr trunk,并将在1.3版本中正式发布。
如果不研究开发Solr,只是使用Solr,只需要关注Solr的以下几个方面:
1、Solr服务器的配置在solrconfig.xml中完成,包括对缓存,servlet的个性化配置等等,即系统全局的配置;
2、索引方法、索引域(字段)等等在schema.xml中完成,这个配置是针对Solr实例的;
3、索引数据文件默认放在Solr文档根目录下的data/index目录下,这个路径可以通过第1点配置,同时可以将这个目录下的文件进行复制粘贴,即可完成索引的复用;
4、建立索引的时间相当长,我采用按词无字典索引方式对2G110万条中文记录进行索引,花了将近2个半小时的时间(当然这个时间和很多因素有关,有兴趣的话大家可以留言和我讨论),相对而言,在linux下建索引时间要比windows下快很多,可以使用commit操作使新增索引生效,同时注意索引的优化,索引优化也是很费资源和时间的,但是优化索引也是提高检索速度的重要方法,因此需要好好权衡这一点;
5、安装完后的Solr目录下有这么几个文件夹:bin文件夹里主要是用于建立镜像和完成远程同步的脚本;conf文件夹下主要是1、2点中提到的配置文件;admin文件夹下是的主要是提供web管理界面的文件;
6、目前Solr1.2不具备安全性设计,没有用户组及权限设置,在进行具体应用时需要注意安全,目前最有效的方法是通过应用服务器上的授权实现。
本文永久链接:http://www.jinsehupan.com/blog/?p=25
[Solr的安装]Solr发行版中已经有一个使用Jetty为servlet容器的小例子,可以使用这个例子来体验,那正在在自己想部署的平台和应用服务器上该怎么一个步骤呢?
要开始使用 Solr,需安装以下软件:
1、Java 1.5 或更高版本;
2、Ant 1.6.x 或更高版本(用于编译管理Solr工程,个人推荐,当然可以使用eclipse);
3、Web 浏览器,用来查看管理页面(官方建议使用Firefox,但实际没有发现和IE有什么差别);
4、servlet 容器,如Tomcat 5.5(不建议使用6版本)。本文以Tomcat 在 8080 端口上运行为例。如果运行的是其他 servlet 容器或在其他的端口上运行,则可能要修改代码中的URL才能访问示例应用程序和 Solr。
下面开始安装配置:
1、使用Ant编译工程或下载示例应用程序,将Solr WAR 文件复制到 servlet 容器的webapps目录中;
2、得到Solr文件夹,以备随后将其复制到当前目录,可以使用ant build得到,也可以在下载的压缩包中找到,以它为模板以备之后的修改;
3、可以通过以下三种方式之一设置 Solr 的主位置:
设置 java 系统属性 solr.solr.home (没错,就是 solr.solr.home,一般在嵌入式集成中用得多);
配置 java:comp/env/solr/home 的一个 JNDI 查找指向 solr 目录,建立/tomcat55/conf/Catalina/localhost/solr.xml文件,注意这个xml文件名将是Solr实例名称,2中的当前目录被指定为下面中的f:/solrhome,文件内容如下:
- <context docBase="f:/solr.war" debug="0" crossContext="true" >
- <environment name="solr/home" type="java.lang.String" value="f:/solrhome" override="true" />
- </context>
在包含 solr 目录的目录中启动 servlet 容器(默认的 Solr 主目录是当前工作目录下的 solr);
4、最后一点就是如果有CJK(中日韩文字)应用,出现乱码问题,采用如下方法解决(其实已经不算是solr配置问题,而是应用服务器配置问题),修改Tomcat的conf/server.xml文件中对于端口(本文为8080)的连接器统一资源编码为UTF-8,因为Solr1.2内核支持UTF-8编码:
- <server ...>
- <service ...>
- <connector ... URIEncoding="UTF-8"/>
- ...
- </service>
- </server>
[Solr分词顺序]Solr建立索引和对关键词进行查询都得对字串进行分词,在向索引库中添加全文检索类型的索引的时候,Solr会首先用空格进行分词,然后把分词结果依次使用指定的过滤器进行过滤,最后剩下的结果才会加入到索引库中以备查询。分词的顺序如下:
索引
1:空格whitespaceTokenize
2:过滤词StopFilter
3:拆字WordDelimiterFilter
4:小写过滤LowerCaseFilter
5:英文相近词EnglishPorterFilter
6:去除重复词RemoveDuplicatesTokenFilter
查询
1:查询相近词
2:过滤词
3:拆字
4:小写过滤
5:英文相近词
6:去除重复词
以上是针对英文,中文的除了空格,其他都类似
[Solr中文应用的一个实例]
1、首先配置schema.xml,这个相当于数据表配置文件,它定义了加入索引的数据的数据类型的。1.2版本的schema.xml主要包括types、fields和其他的一些缺省设置。
A、首先需要在types结点内定义一个FieldType子结点,包括name,class,positionIncrementGap等等一些参数,name就是这个FieldType的名称,class指向org.apache.solr.analysis包里面对应的class名称,用来定义这个类型的行为。在FieldType定义的时候最重要的就是定义这个类型的数据在建立索引和进行查询的时候要使用的分析器analyzer,包括分词和过滤。在例子中text这个FieldType在定义的时候,在index的analyzer中使用solr.WhitespaceTokenizerFactory这个分词包,就是空格分词,然后使用solr.StopFilterFactory,solr.WordDelimiterFilterFactory,solr.LowerCaseFilterFactory,solr.EnglishPorterFilterFactory,solr.RemoveDuplicatesTokenFilterFactory这几个过滤器。在向索引库中添加text类型的索引的时候,Solr会首先用空格进行分词,然后把分词结果依次使用指定的过滤器进行过滤,最后剩下的结果才会加入到索引库中以备查询。Solr的analysis包并没有带支持中文的包,在这里我们采用lucene里的语言包(在下载后的solr压缩包内,lib目录下有一个lucene-analyzers-2.2.0.jar包,里面含有中文处理的cn和cjk类),有cn和cjk两个类可以支持中文。我们采用cjk类,并在schema.xml中加入如下配置:
- <fieldtype name="text_cjk" class="solr.TextField">
- <analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer"/>
- </fieldtype>
支持类型定义完成了。
B、接下来的工作就是在fields结点内定义具体的字段(类似数据库中的字段),就是filed,filed定义包括name,type(为之前定义过的各种FieldType),indexed(是否被索引),stored(是否被储存),multiValued(是否有多个值)等等。例如定义如下:
- <field name="记录号" type="slong" indexed="true" stored="true" required="true" />
- <field name="文件名" type="string" indexed="true" stored="true" />
- <field name="日期" type="date" indexed="true" stored="true" />
- <field name="版次" type="string" indexed="true" stored="true" multiValued="true"/>
- <field name="栏目" type="string" indexed="true" stored="true" multiValued="true"/>
- <field name="标题" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
- <field name="作者" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
- <field name="正文" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
- <field name="标记" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
field的定义相当重要,有几个技巧需注意一下,对可能存在多值得字段尽量设置multiValued属性为true,避免建索引是抛出错误;如果不需要存储相应字段值,尽量将stored属性设为false。
C、建议建立了一个拷贝字段,将所有的全文字段复制到一个字段中,以便进行统一的检索:
- <field name="text_com" type="text_cjk" indexed="true" stored="false" multiValued="true"/>
并在拷贝字段结点处完成拷贝设置:
- <copyfield source="标题" dest="text_com"/>
- <copyfield source="正文" dest="text_com"/>
D、除此之外,还可以定义动态字段,所谓动态字段就是不用指定具体的名称,只要定义字段名称的规则,例如定义一个dynamicField,name为*_i,定义它的type为text,那么在使用这个字段的时候,任何以_i结尾的字段都被认为是符合这个定义的,例如name_i,gender_i,school_i等。
2、配置solrconfig.xml,用来配置Solr的一些系统属性,比较重要的一个就是可以通过更改其中的dataDir属性来指定索引文件的存放位置,对于有大数据量的情况下还要进行自动commit操作配置,以下设置为当内存索引量达到20W条时自动进行往磁盘写操作,以免堆溢出,这也是解决单个入库xml文件最好不要超过30M的有效方法:
- <autocommit>
- <maxdocs>200000</maxdocs>
- </autocommit>
3、配置好这些后,需要重新启动Solr服务器使配置生效,然后向其中添加数据。
4、添加数据是通过向服务器的update Servlet POST xml格式的数据来实现的,xml结构是这样的add中间有很多个doc,每个doc中有很多个field。添加到索引库中的每条记录都必须指定唯一的数字id来唯一标识这条索引。建立好xml文件(例如solr.xml)之后,在exampledocs目录下执行:java -jar post.jar solr.xml来添加索引数据。对于post的jar包,如果重新配置了应用服务器,如使用了comcat,端口改为8080,实例名称改为solrx了需要重新生成相应的post.jar包进行操作。
另附ronghao实现中文分词的案例供大家参考:
对全文检索而言,中文分词非常的重要,这里采用了qieqie庖丁分词(非常不错:))。集成非常的容易,我下载的是2.0.4-alpha2版本,其中它支持最多切分和按最大切分。创建自己的一个中文TokenizerFactory继承自solr的BaseTokenizerFactory。
**
* Created by IntelliJ IDEA.
* User: ronghao
* Date: 2007-11-3
* Time: 14:40:59
* 中文切词 对庖丁切词的封装
*/
public class ChineseTokenizerFactory extends BaseTokenizerFactory {
/**
* 最多切分 默认模式
*/
public static final String MOST_WORDS_MODE = “most-words”;
/**
* 按最大切分
*/
public static final String MAX_WORD_LENGTH_MODE = “max-word-length”;
private String mode = null;
public void setMode(String mode) {
if (mode==null||MOST_WORDS_MODE.equalsIgnoreCase(mode)
|| “default”.equalsIgnoreCase(mode)) {
this.mode=MOST_WORDS_MODE;
} else if (MAX_WORD_LENGTH_MODE.equalsIgnoreCase(mode)) {
this.mode=MAX_WORD_LENGTH_MODE;
}
else {
throw new IllegalArgumentException(”不合法的分析器Mode参数设置:” + mode);
}
}
@Override
public void init(Map args) {
super.init(args);
setMode(args.get(”mode”));
}
public TokenStream create(Reader input) {
return new PaodingTokenizer(input, PaodingMaker.make(),
createTokenCollector());
}
private TokenCollector createTokenCollector() {
if( MOST_WORDS_MODE.equals(mode))
return new MostWordsTokenCollector();
if( MAX_WORD_LENGTH_MODE.equals(mode))
return new MaxWordLengthTokenCollector();
throw new Error(”never happened”);
}
}
在schema.xml的字段text配置里加入该分词器。
1. <fieldtype name="text" class="solr.TextField" positionIncrementGap="100">
2.
3. <analyzer type="index">
4.
5. <tokenizer class="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory" mode="most-words"/>
6.
7.
8. <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
9.
10. <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
11.
12. <filter class="solr.LowerCaseFilterFactory"/>
13.
14.
15. <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
16.
17. </analyzer>
18.
19. <analyzer type="query">
20.
21. <tokenizer class="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory" mode="most-words"/>
22.
23. <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
24.
25. <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
26.
27. <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0"/>
28.
29. <filter class="solr.LowerCaseFilterFactory"/>
30.
31. <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
32.
33. </analyzer>
34.
35. </fieldtype>
完成后重启tomcat,即可在http://localhost:8080/solr/admin/analysis.jsp
体验到庖丁的中文分词。注意要将paoding-analysis.jar复制到solr的lib下,注意修改jar包里字典的home。
[Solr的检索运算符]
“:” 指定字段查指定值,如返回所有值*:*²
“?”²表示单个任意字符的通配
“*” 表示多个任意字符的通配(不能在检索的项开始使用*或者?符号)²
“~”²表示模糊检索,如检索拼写类似于”roam”的项这样写:roam~将找到形如foam和roams的单词;roam~0.8,检索返回相似度在0.8以上的记录。
²邻近检索,如检索相隔10个单词的”apache”和”jakarta”,”jakarta apache”~10
“^”²控制相关度检索,如检索jakarta apache,同时希望去让”jakarta”的相关度更加好,那么在其后加上”^”符号和增量值,即jakarta^4 apache
布尔操作符AND、||²
布尔操作符OR、²&&
布尔操作符NOT、!、-²(排除操作符不能单独与项使用构成查询)
“+” 存在操作符,要求符号”+”后的项必须在文档相应的域中存在²
( ) 用于构成子查询²
² [] 包含范围检索,如检索某时间段记录,包含头尾,date:[200707 TO 200710]
{}²不包含范围检索,如检索某时间段记录,不包含头尾
date:{200707 TO 200710}
" 转义操作符,特殊字符包括+ -² && || ! ( ) { } [ ] ^ ” ~ * ? : "
posted @
2008-03-26 11:22 流浪汗 阅读(14371) |
评论 (1) |
编辑 收藏

2008年3月24日
转载: http://www.math.ecnu.edu.cn/~jypan/linux/command/rpm.htm
RPM 命令
一、RPM介绍
1.什么是rpm ?
rpm 即RedHat Package Management,是RedHat的发明之一
2.为什么需要rpm ?
在一个操作系统下,需要安装实现各种功能的软件包。这些软件包一般都有各自的程序,
但是同时也有错综复杂的依赖关系。同时还需要 解决软件包的版本,以及安装,配置,
卸载的自动化问题。为了解决 这些问题,RedHat针对自己的系统提出了一个较好的办法
来管理成千 上百的软件。这就是RPM管理系统。在系统中安装了rpm管理系统以后,
只要是符合rpm文件标准的打包程序都可以方便地安装、升级、卸载。
3.是不是所有的linux都使用rpm ?
任何系统都需要包管理系统,因此很多linux都使用rpm系统。 rpm系统是Redhat Linux和
Fedora Core的软件包管理器,但是Mandriva、SuSE等Linux发行版也都使用rpm。由于rpm
的源程序可以在别的系统上进行编译,所以有可能在别的系统上也使用rpm。除了rpm,
其他一些系统也有自己的软件包管理程序, 例如 debian的deb包。
4.rpm包的文件名为什么那么长 ?
rpm包的文件名中包含了这个软件包的版本信息,操作系统信息,硬件要求等等。
比如mypackage-1.1-2RH.i386.rpm,其中mypackage是在系统中登记的软件包的名字1.1
是软件的版本号,2是发行号,RH表示用于RH操作系统。i386表示用于intel x86平台。
5.软件包文件名中的i386,i686是什么意思
rpm软件包的文件名中,不仅包含了软件名称,版本信息,还包括了适用的硬件架构的信息。
i386指这个软件包适用于intel 80386以上的x86架构的计算机(AI32)
i686指这个软件包适用于intel 80686以上(奔腾pro以上)的x86架构的计算机(IA32)
noarch指这个软件包与硬件架构无关,可以通用。
i686软件包通常针对CPU进行了优化,现在通常配置的机器都可以使用i686软件包。
6.不同操作系统发行的rpm包可否混用?
对于已经编译成二进制的rpm包,由于操作系统环境不同,一般不能混用。
对于以src.rpm发行的软件包,由于需要安装时进行本地编译,所以通常可以在不同系统下安装。
二、RPM包管理的用途
1、可以安装、删除、升级和管理以rpm包形式发布的软件;
2、可以查询某个rpm包中包含哪些文件,以及某个指定文件属于哪个rpm包;
3、可以在查询系统中的某个rpm包是否已安装以及其版本;
4、作为开发者可以把自己开发的软件打成rpm包发布;
5、依赖性的检查,查询安装某个rpm包时,需要哪些其它的rpm包。
注:RPM软件的安装、删除、更新只有root权限才能使用;
对于查询功能任何用户都可以操作。
三、rpm 的一点简单用法
rpm的一般格式:
1、初始化rpm 数据库(可以省略)
rpm --initdb
rpm --rebuilddb % 注:这个要花好长时间
%注:有时rpm 系统出了问题,不能安装和查询,大多是这里出了问题。
2、RPM软件包管理的查询功能:
rpm -q [select-options] [query-options]
RPM的查询功能是极为强大,是极为重要的功能之一;这里举几个常用的例子,更为详细的具体的,请参考 man rpm
对系统中已安装软件的查询
1)查询系统已安装的软件
语法:rpm -q 软件名
例:rpm -q mplayer
% -q就是 --query,此选项表示询问系统是不是安装了gaim软件包;
% 如果已安装会有信息输出;如果没有安装,会输出gaim 没有安装的信息;
%查看系统中所有已经安装的包,要加 -a 参数
rpm -qa
% 如果分页查看,再加一个管道 |和more命令
rpm -qa |more
% 如果要查找某个软件包,可以用 grep 抽取出来
rpm -qa |grep mplayer
2)查询一个已经安装的文件属于哪个软件包;
语法: rpm -qf 文件名
注:文件名所在的绝对路径要指出
例:rpm -qf /usr/lib/libacl.la
3)查询已安装软件包都安装到何处;
语法:rpm -ql 软件包名
例:rpm -ql mplayer
4)查询一个已安装软件包的信息
语法: rpm -qi 软件包名
例:rpm -qi mplayer
5)查看一下已安装软件的配置文件;
语法格式:rpm -qc 软件名
例:rpm -qc mplayer
6)查看一个已经安装软件的文档安装位置:
语法格式: rpm -qd 软件名
例:rpm -qd mplayer
7)查看一下已安装软件所依赖的软件包及文件;
语法格式: rpm -qR 软件名
例:rpm -qR mplayer
注:可以把几个参数组合起来用,如 rpm -qil mplayer
对于未安装的软件包的查看 :查看的前提是当前目录下已存在一个.rpm文件。
1)查看一个软件包的用途、版本等信息;
语法: rpm -qpi file.rpm
例:rpm -qpi mplayer-1.0pre7try2-2.i386.rpm
2)查看一件软件包所包含的文件;
语法: rpm -qpl file.rpm
例:rpm -qpl mplayer-1.0pre7try2-2.i386.rpm
3)查看软件包的文档所在的位置;
语法: rpm -qpd file.rpm
例:rpm -qpd mplayer-1.0pre7try2-2.i386.rpm
4)查看一个软件包的配置文件;
语法: rpm -qpc file.rpm
例:rpm -qpc mplayer-1.0pre7try2-2.i386.rpm
5)查看一个软件包的依赖关系
语法: rpm -qpR file.rpm
例:rpm -qpR mplayer-1.0pre7try2-2.i386.rpm
3、软件包的安装、升级、删除等; 安装和升级一个rpm 包
语法:
rpm -ivh file.rpm % 这个是用来安装一个新的rpm 包
rpm -Uvh file.rpm % 这是用来升级一个rpm 包
% 如果有依赖关系的,需解决依赖关系。
% 如果找不到依赖关系的包,可以用下面的命令强制安装:
rpm -ivh --nodeps --force file.rpm
rpm -Uvh --nodeps --force file.rpm
例:
rpm -ivh --test mplayer-1.0pre7try2-2.i386.rpm
% --test表示测试,并不真正安装。
rpm -ivh --relocate /=/usr/local/mplayer mplayer-1.0pre7try2-2.i386.rpm
% 为软件包指定安装目录:要加 --relocate 参数
% 安装在指定目录中的程序如何调用呢?
% 通常可执行程序都放在安装目录下的bin或者sbin目录中。
删除一个rpm 包
首先查出需要删除的rpm包,然后用下面的命令来卸载:
rpm -e 软件包名
例:rpm -e mplayer % 卸载mplayer
% 如果有其它的rpm依赖于该rpm包,系统会出现警告。
% 如果一定要卸载,可以用选项 --nodeps 忽略依赖关系。但最好不要这么做。
四、RPM管理包管理器支持网络安装和查询
rpm [选项] rpm包的http或者ftp的地址
比如我们想通过 Fedora Core 4.0 的一个镜像查询、安装软件包。
rpm -qpi http://mirrors.kernel.org/.../RPMS/rsh-0.17-29.rpm
% 查询
rpm -ivh http://mirrors.kernel.org/.../RPMS/rsh-0.17-29.rpm
% 安装
五、对已安装rpm包查询的一点补充
可以用 locate来查询一些软件的安装位置,可能需要先运行updatedb来更新已安装软件库
六、从rpm软件包抽取文件
rpm2cpio xxx.rpm | cpio -idmv
% i 表示提取文件,v 表示指示执行进程
% d 表示根据包中文件原来的路径建立目录
% m 表示保持文件的更新时间
posted @
2008-03-24 14:17 流浪汗 阅读(2416) |
评论 (1) |
编辑 收藏
摘要: 转载: http://www.math.ecnu.edu.cn/~jypan/linux/command/
Linux 常用命令
Linux 系统常用命令格式:
...
阅读全文
posted @
2008-03-24 11:40 流浪汗 阅读(335) |
评论 (0) |
编辑 收藏
转载:
http://www.math.ecnu.edu.cn/~jypan/linux/command/find.htm
find 命令
在目录结构中搜索文件,并执行指定的操作。此命令提供了相当多的查找条件,功能很强大。
说明:find命令从指定的起始目录开始,递归地搜索其各个子目录,查找满足寻找条件 的文件并对之采取相关的操作。该命令提供的寻找条件可以是一个用逻辑运算符 not、and、or 组成的复合条件。逻辑运 算符 and、or、not 的含义为:
(1) and:逻辑与,在命令中用 -a 表示,是系统缺省的选项,表示只有当所给的条件都满足时,寻找条件才算满足。例如:
find -name 'tmp' -xtype c -user 'inin'
% 该命令寻找三个给定条件都满足的所有文件
(2) or:逻辑或,在命令中用 -o 表示。该运算符表示只要所给的条件中有一个满足时,寻找条件就算满足。例如:
find -name 'tmp' -o -name 'mina*'
% 该命令查询文件名为 tmp 或是匹配 mina* 的所有文件。
(3) not:逻辑非,在命令中用 ! 表示。该运算符表示查找不满足所给条件的文件。例如:
find ! -name 'tmp'
% 该命令查询文件名不是 tmp 的所有文件。
需要说明的是:当使用很多的逻辑选项时,可以用括号把这些选项括起来。为了避免 Shell本身对括号引起误解,在话号前需要加转义字符 " 来去除括号的意义。例:
find "( -name 'tmp' -xtype c -user 'inin' ")
该命令的各选项含义如下:
首先,下列各个选项中的 n 值可以有三种输入方式,假设 n 为20,则:
+20 表示20以后(21,22,23等)
-20 表示20以前(19,18,17等)
20 表示正好是20
以名称和文件属性查找。
-name '字串' 查找文件名匹配所给字串的所有文件,字串内可用通配符 *、?、[ ]。
-lname '字串' 查找文件名匹配所给字串的所有符号链接文件,字串内可用通配符 *、?、[ ]。
-gid n 查找属于ID号为 n 的用户组的所有文件。
-uid n 查找属于ID号为 n 的用户的所有文件。
-group '字串' 查找属于用户组名为所给字串的所有的文件。
-user '字串' 查找属于用户名为所给字串的所有的文件。
-empty 查找大小为 0的目录或文件。
-path '字串' 查找路径名匹配所给字串的所有文件,字串内可用通配符*、?、[ ]。
-perm 权限 查找具有指定权限的文件和目录,权限的表示可以如711,644。
-size n[bckw] 查找指定文件大小的文件,n 后面的字符表示单位,缺省为 b,代表512字节的块。
-type x 查找类型为 x 的文件,x 为下列字符之一:
b 块设备文件
c 字符设备文件
d 目录文件
p 命名管道(FIFO)
f 普通文件
l 符号链接文件(symbolic links)
s socket文件
-xtype x 与 -type 基本相同,但只查找符号链接文件。
以时间为条件查找
-amin n 查找n分钟以前被访问过的所有文件。
-atime n 查找n天以前被访问过的所有文件。
-cmin n 查找n分钟以前文件状态被修改过的所有文件。
-ctime n 查找n天以前文件状态被修改过的所有文件。
-mmin n 查找n分钟以前文件内容被修改过的所有文件。
-mtime n 查找n天以前文件内容被修改过的所有文件。
可执行的操作
-exec 命令名称 {} 对符合条件的文件执行所给的Linux 命令,而不询问用户是否需要执行该命令。
{}表示命令的参数即为所找到的文件;命令的末尾必须以“ ";”结束。
-ok 命令名称 {} 对符合条件的文件执行所给的Linux 命令,与exec不同的是,
它会询问用户是否需要执行该命令。
-ls 详细列出所找到的所有文件。
-fprintf 文件名 将找到的文件名写入指定文件。
-print 在标准输出设备上显示查找出的文件名。
-printf 格式 格式的写法请参考有关C语言的书。
指令实例:
find . -name ‘main*' -exec more {} ";
% 查找当前目录中所有以main开头的文件,并显示这些文件的内容。
find . "( -name a.out -o -name ‘*.o'") -atime +7 -exec rm {} ";
% 删除当前目录下所有一周之内没有被访问过的 a .out 或 *.o 文件。
% 命令中的“.”表示当前目录,此时 find 将从当前目录开始,
逐个在其子目录中查找满足后面指定条件的文件。
% “"(” 和 “")” 表示括号(),其中的 “"” 称为转义符。之所以这样写是由于
对 Shell 而言,(和)另有不同的含义,而不是这里的用于组合条件的用途。
% “-name a.out” 是指要查找名为a.out的文件;
% “-name ‘*.o'” 是指要查找所有名字以 .o 结尾的文件。
这两个 -name 之间的 -o 表示逻辑或(or),即查找名字为a.out或名字以 .o结尾的文件。
% find在当前目录及其子目录下找到这佯的文件之后,再进行判断,看其最后访问时间
是否在7天以前(条件 -atime +7),若是,则对该文件执行删除命令(-exec rm {} ";)。
其中 {} 代表当前查到的符合条件的文件名,";则是语法所要求的。
posted @
2008-03-24 10:10 流浪汗 阅读(614) |
评论 (0) |
编辑 收藏

2008年3月17日
转载:
http://www.cnblogs.com/wjun530/archive/2007/06/14/782898.html
彻底明白Java的IO系统
一. Input和Output
1. stream代表的是任何有能力产出数据的数据源,或是任何有能力接收数据的接收源。在Java的IO中,所有的stream(包括Input和Out stream)都包括两种类型:
1.1 以字节为导向的stream
以字节为导向的stream,表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型:
1) input stream:
1) ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用
2) StringBufferInputStream:把一个String对象作为InputStream
3) FileInputStream:把一个文件作为InputStream,实现对文件的读取操作
4) PipedInputStream:实现了pipe的概念,主要在线程中使用
5) SequenceInputStream:把多个InputStream合并为一个InputStream
2) Out stream
1) ByteArrayOutputStream:把信息存入内存中的一个缓冲区中
2) FileOutputStream:把信息存入文件中
3) PipedOutputStream:实现了pipe的概念,主要在线程中使用
4) SequenceOutputStream:把多个OutStream合并为一个OutStream
1.2 以Unicode字符为导向的stream
以Unicode字符为导向的stream,表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型:
1) Input Stream
1) CharArrayReader:与ByteArrayInputStream对应
2) StringReader:与StringBufferInputStream对应
3) FileReader:与FileInputStream对应
4) PipedReader:与PipedInputStream对应
2) Out Stream
1) CharArrayWrite:与ByteArrayOutputStream对应
2) StringWrite:无与之对应的以字节为导向的stream
3) FileWrite:与FileOutputStream对应
4) PipedWrite:与PipedOutputStream对应
以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同,字是在操作时的导向不同。如CharArrayReader:和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用,所不同的是前者每次从内存中读取一个字节的信息,而后者每次从内存中读取一个字符。
1.3 两种不现导向的stream之间的转换
InputStreamReader和OutputStreamReader:把一个以字节为导向的stream转换成一个以字符为导向的stream。
2. stream添加属性
2.1 “为stream添加属性”的作用
运用上面介绍的Java中操作IO的API,我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类,我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。
如果我们要往一个文件中写入数据,我们可以这样操作:
FileOutStream fs = new FileOutStream(“test.txt”);
然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是,如果我们想实现“先把要写入文件的数据先缓存到内存中,再把缓存中的数据写入文件中”的功能时,上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类,为FileOutStream添加我们所需要的功能。
2.2 FilterInputStream的各种类型
2.2.1 用于封装以字节为导向的InputStream
1) DataInputStream:从stream中读取基本类型(int、char等)数据。
2) BufferedInputStream:使用缓冲区
3) LineNumberInputStream:会记录input stream内的行数,然后可以调用getLineNumber()和setLineNumber(int)
4) PushbackInputStream:很少用到,一般用于编译器开发
2.2.2 用于封装以字符为导向的InputStream
1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader,否则使用DataInputStream
2) BufferedReader:与BufferedInputStream对应
3) LineNumberReader:与LineNumberInputStream对应
4) PushBackReader:与PushbackInputStream对应
2.3 FilterOutStream的各种类型
2.2.3 用于封装以字节为导向的OutputStream
1) DataIOutStream:往stream中输出基本类型(int、char等)数据。
2) BufferedOutStream:使用缓冲区
3) PrintStream:产生格式化输出
2.2.4 用于封装以字符为导向的OutputStream
1) BufferedWrite:与对应
2) PrintWrite:与对应
3. RandomAccessFile
1) 可通过RandomAccessFile对象完成对文件的读写操作
2) 在产生一个对象时,可指明要打开的文件的性质:r,只读;w,只写;rw可读写
3) 可以直接跳到文件中指定的位置
4. I/O应用的一个例子
import java.io.*;
public class TestIO{
public static void main(String[] args)
throws IOException{
//1.以行为单位从一个文件读取数据
BufferedReader in =
new BufferedReader(
new FileReader("F:\\nepalon\\TestIO.java"));
String s, s2 = new String();
while((s = in.readLine()) != null)
s2 += s + "\n";
in.close();
//1b. 接收键盘的输入
BufferedReader stdin =
new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Enter a line:");
System.out.println(stdin.readLine());
//2. 从一个String对象中读取数据
StringReader in2 = new StringReader(s2);
int c;
while((c = in2.read()) != -1)
System.out.println((char)c);
in2.close();
//3. 从内存取出格式化输入
try{
DataInputStream in3 =
new DataInputStream(
new ByteArrayInputStream(s2.getBytes()));
while(true)
System.out.println((char)in3.readByte());
}
catch(EOFException e){
System.out.println("End of stream");
}
//4. 输出到文件
try{
BufferedReader in4 =
new BufferedReader(
new StringReader(s2));
PrintWriter out1 =
new PrintWriter(
new BufferedWriter(
new FileWriter("F:\\nepalon\\ TestIO.out")));
int lineCount = 1;
while((s = in4.readLine()) != null)
out1.println(lineCount++ + ":" + s);
out1.close();
in4.close();
}
catch(EOFException ex){
System.out.println("End of stream");
}
//5. 数据的存储和恢复
try{
DataOutputStream out2 =
new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("F:\\nepalon\\ Data.txt")));
out2.writeDouble(3.1415926);
out2.writeChars("\nThas was pi:writeChars\n");
out2.writeBytes("Thas was pi:writeByte\n");
out2.close();
DataInputStream in5 =
new DataInputStream(
new BufferedInputStream(
new FileInputStream("F:\\nepalon\\ Data.txt")));
BufferedReader in5br =
new BufferedReader(
new InputStreamReader(in5));
System.out.println(in5.readDouble());
System.out.println(in5br.readLine());
System.out.println(in5br.readLine());
}
catch(EOFException e){
System.out.println("End of stream");
}
//6. 通过RandomAccessFile操作文件
RandomAccessFile rf =
new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");
for(int i=0; i<10; i++)
rf.writeDouble(i*1.414);
rf.close();
rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");
for(int i=0; i<10; i++)
System.out.println("Value " + i + ":" + rf.readDouble());
rf.close();
rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");
rf.seek(5*8);
rf.writeDouble(47.0001);
rf.close();
rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");
for(int i=0; i<10; i++)
System.out.println("Value " + i + ":" + rf.readDouble());
rf.close();
}
}
关于代码的解释(以区为单位):
1区中,当读取文件时,先把文件内容读到缓存中,当调用in.readLine()时,再从缓存中以字符的方式读取数据(以下简称“缓存字节读取方式”)。
1b区中,由于想以缓存字节读取方式从标准IO(键盘)中读取数据,所以要先把标准IO(System.in)转换成字符导向的stream,再进行BufferedReader封装。
2区中,要以字符的形式从一个String对象中读取数据,所以要产生一个StringReader类型的stream。
4区中,对String对象s2读取数据时,先把对象中的数据存入缓存中,再从缓冲中进行读取;对TestIO.out文件进行操作时,先把格式化后的信息输出到缓存中,再把缓存中的信息输出到文件中。
5区中,对Data.txt文件进行输出时,是先把基本类型的数据输出屋缓存中,再把缓存中的数据输出到文件中;对文件进行读取操作时,先把文件中的数据读取到缓存中,再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble(),所以为了正确显示。也要以基本类型的形式进行读取。
6区是通过RandomAccessFile类对文件进行操作。
----
重要提示:
LineNumberInputStream,StringBufferInputStream已经废除!大家不要再用!
StringBufferInputStream,This class does not properly convert characters into bytes!
StringBufferInputStream,Deprecated. This class incorrectly assumes that bytes adequately represent characters!
----------------关心IO,就是关心你的JAVA前途之路!-----------------------
DataInputStream流中已经没有readLine()整个方法!
替换为: BufferedReader d=new BufferedReader(new InputStreamReader(in));
--------把字节流转换为字符流接入缓存读取字符流中,再进行处理!
这个方法很大的优势!
----------------------
-----------------Why use character streams?------------------
The primary advantage of character streams is that they make it easy to write programs that are not dependent upon a specific character encoding, and are therefore easy to internationalize.
Java stores strings in Unicode, an international standard character encoding that is capable of representing most of the world's written languages. Typical user-readable text files, however, use encodings that are not necessarily related to Unicode, or even to ASCII, and there are many such encodings. Character streams hide the complexity of dealing with these encodings by providing two classes that serve as bridges between byte streams and character streams. The InputStreamReader class implements a character-input stream that reads bytes from a byte-input stream and converts them to characters according to a specified encoding. Similarly, the OutputStreamWriter class implements a character-output stream that converts characters into bytes according a specified encoding and writes them to a byte-output stream.
A second advantage of character streams is that they are potentially much more efficient than byte streams. The implementations of many of Java's original byte streams are oriented around byte-at-a-time read and write operations. The character-stream classes, in contrast, are oriented around buffer-at-a-time read and write operations. This difference, in combination with a more efficient locking scheme, allows the character stream classes to make up for the added overhead of encoding conversion in many cases.
----------标准设备System.in读取数据------------------
-----------------------------------------------------
读取字节:BufferedInputStream
读取字符:BufferedReader + InputStreamReader
----------------------------------------------
import java.io.*;
public class systemin
{
public static void main(String args[])
{ try{ //流转换!
BufferedReader is=new BufferedReader(new InputStreamReader(System.in))
String inputline=null;
while((inputline=is.readLine())!=null)
System.out.println(inputline);
is.close();
}
catch(IOException e)
{ System,out.println("IOXE: "+e);
}
}
}
--------------------------------------------------------------------------------
-----------------标准输出System.out是一个打印流PrintStream---------------------
import java.io.*;
public class PrintStandardOutput {
public static void main(String[] args) {
String myAnswer = "No, and that's final,";
System.out.println("Hello World of Java");
System.out.println("The answer is " + myAnswer + " at this time.");
PrintWriter pw = new PrintWriter(System.out);
pw.println("The answer is " + myAnswer + " at this time.");
int i = 42;
pw.println(i + '=' + " the answer.");
pw.println("Note: " + i + '=' + " the answer.");
pw.println(i + "=" + " the answer.");
pw.println(i + ('=' + " the answer."));
pw.close();
}
}
-----------------------------------------------------------------------------------
-----------------------要读取(输出到—)一个文本文件-----------------------------
BufferedReader is=new BufferedReader(new FileReader("xxxx.text"));读取
BufferedOutputStream byteout=new BufferedOutputStream(new FileOutputStream("XX.dat"));
// 写出到文本!
-----------------------------------------------
---------------利用 BufferedReader--FileReader读取文本文件!-----------
---------------在IO中始终要注意是字节流还是字符流----------------------
-----------------------------------------------------------------------
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
class filewindow extends JFrame implements ActionListener
{
JTextArea text;
BufferedReader in;
JButton button;
FileReader file;
filewindow()
{
super("文件字符流");
Container con=getContentPane();
text=new JTextArea(50,50);
text.setBackground(Color.blue);
try{
File f=new File("E:\\a.txt");
file=new FileReader(f);
in=new BufferedReader(file);
/**BufferedReader(Reader in)构造函数,
*文件自字符读取流FileReader接入BufferedReader
*流中,以便用BufferedReader的对象方法readLine()高效成行读取!
*/
}
catch(FileNotFoundException e){}
catch(IOException e){}
button=new JButton("读取");
button.addActionListener(this);
con.setLayout(new BorderLayout());
setSize(300,200);
setVisible(true);
con.add(text,"Center");
con.add(button,"South");
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e)
{setVisible(false);System.exit(0);}});
}
public void actionPerformed(ActionEvent e)
{
String s;
if(e.getSource()==button)
try{
while((s=in.readLine())!=null)
text.append(s+'\n');
//在这里大家还可以用BufferString来暂时保存读取的字符数据!
}
catch(IOException e1){}
}
//---------main()----------
public static void main(String args[])
{
filewindow win=new filewindow();
win.pack();
}
}
-------------------RandomAccessFile随机读取文件---------------
import java.io.*;
public class RandomRead
{
final static String FILENAME="E:\\a.txt";
protected String fileName;
protected RandomAccessFile seeker;
public static void main(String[] argv) throws IOException {
RandomRead r = new RandomRead(FILENAME);
System.out.println("Offset is " + r.readOffset());
System.out.println("Message is \"" + r.readMessage() + "\".");
}
/** Constructor: save filename, construct RandomAccessFile */
public RandomRead(String fname) throws IOException {
fileName = fname;
seeker = new RandomAccessFile(fname, "rw");
}
/** Read the Offset field, defined to be at location 0 in the file. */
public int readOffset() throws IOException {
seeker.seek(0);
seeker.writeChars(FILENAME); // move to very beginning
return seeker.readInt(); // and read the offset
}
/** Read the message at the given offset */
public String readMessage() throws IOException {
seeker.seek(120); // move to where
return seeker.readLine(); // and read the String
}
}
写得很辛苦,我本来不想再说什么了,但本着对技术负责的精神还是说出来.
对于I/O的理解属于3级水平(如果java IO有十级的话)
错误太多.
对于I/O层次不熟悉
java IO主要包括
java.io包和java.nio包.
java.io主要从四个接口延伸:
字节:
InputStream/OutputStream,其下为封装,过滤,特定对象处理的具体实现类.
字符:
Reader/Writer(原文中连Writer接口全都写成Write,足以说明根本不了解这了接口,如果你经常使用Writer接口怎么会连Writer和Write都分不清,不是一处失误,而是全部都是Write)
以上四个接口中,底层全部是操作字节的阻塞方式流.
java.nio主要以块操作块(Buffer)为主,通过可以设定的阻塞和非阴塞模式,极大地提过了数据输出输
入的性能,而且将Channel通过选选择器模式的控制,可以实现在同一输出输入通道上多用户并发进行数据传输入.比如一个Socket端口可以同时被无限多(理论上不受限制)个客户端并发访问.就是经典的I/O多路复用技术.
posted @
2008-03-17 14:26 流浪汗 阅读(865) |
评论 (0) |
编辑 收藏