博文

JavaScript收藏(2006-12-14 21:23:00)

随着ajax的升温,javascript越来越得到人们的重视。重要的是,ajax在一定程度上带来了web软件架构上的变化,人们把越来越多的功能分配到客户端实现,javascript子项目规模越来越大。如何更高效的使用javascript,如何更科学的组织javascript,如何更顺利的保证项目进展?我想就我的经验谈一点浅见。

一。 开发人员需要认真学习javascript语言本身
       由于javascript是“[url =http://www2.uuzone.com/blog/555080192/18957.htm]世界上最被误解的语言[/url]”, 大部分人对javascript语法并没有全面了解过,只是凭借看起来很像c或者java的关键字按照自己的理解写javascript代码。其实 javascript是一种很独特的语言,和c++/java有非常大的区别,要想用javascript做大一些的项目,开发人员必须老老实实的学习 javascript的语法。真正掌握了语法后,我们才不会把delete看成释放内存对象,才不会为到底参数传递是值传递还是引用传递而烦恼。真正理解了javascript的基于原型的OO方式,才可能写出具有良好架构的javascript程序。
       《javascript权威指南》是一本最合适的书,郑重推荐。另外ECMA262 文档可以作为参考。网上流行的jscript手册chm版本使用起来比较方便,不过这是微软的jscript实现,和标准的javascript略有区别,使用时应该注意上面的注脚信息。关于javascript的原型和OO,网上已经有很多文章介绍了,在此不再多说。

二。 良好的代码来源于良好的设计
       只有设计优良,代码才会写的漂亮。现在的javascript子项目已经不是以前web项目中的“边角料”和散兵游勇了,在较大的ajax项目内, javascript将非常复杂,ajax的异步模型也和以前顺序执行的程序设计有所区别。所以建议做javascript前首先做好设计。推荐使用用例驱动的方式,把用例分析清楚,以便全局考虑所有可能的页面交互过程,绘出页面内一些对象之间的交互图,分析一些数据对象的状态,作出精细的 javascript设计。

三。 使用设计模式,复用其他领域的设计经验
        如果javascript非常复杂,可以考虑使用一些模式。我想大部分做javascript的开发者都不是“javascript科班”出身吧 掌握了javascript的语言本质,就可以复用我们在其他领域的经验了。使用javascript框架或者ajax框架,使用单例模式做一个全局的数据缓冲池,或者使用观察者模式把界面对象和数据对象分离,使用命令模式实现用户的操作队列等等。

四。 调试代码的技巧
        javascript的代码不太好调试,这是由于:

    * 一般的开发人员对javascript语言本身不太精通。也就是上面提到的。
    * web项目包含较多的因素,复杂性加剧。服务端脚本、模板、html、js等很多环节都可能增加调试难度。
    * 浏览器存在兼容性问题。有可能在一个细节问题上IE、Mozilla、opera等浏览器都有差异。
    * 工具的缺乏。虽然mozilla的jsdebugger非常好用(还有bug,比如eval时调试器有些问题),但是其他浏览器环境下调试工具就不怎么样了。ms系统自带的script debug工具调试本地代码还可以,直接调试网站js代码表现欠佳。opera除了javascript控制台外我没有找到其他调试工具。

       在此我推荐几个调试技巧:

   1. 使用Mozilla firefox的jsdebugger插件。这个我不再多说了,最经典的js调试工具。在线调试远程站点的javascript效果非常棒。
   2. 把问题隔离,建立本地的html文件和js文件,使用ms script debug调试工具来调试。如果js模块比较独立,可以使用这个工具的。如果写hta的项目,这个工具当然是首选了。
   3. httpWatch 这是一个ie下的插件,非常好用,能够监视ie中的任何http会话,并能够看到http会话的原文。可以通过这个工具了解你的程序有没有和服务器产生会话,参数&返回的数据到底是什么。
   4. 在网页内建立用于调试的textarea
      可以在网页内建立一个textarea来接受你想运行的js语句,然后加一个按钮使用js的eval函数执行你输入的代码。
      这种方式非常适合在线调试,网页出错后写代码输出页面内的对象值。建议写一些dump工具函数配合使用,效果更佳。
      我非常喜欢这种方式,可以随时使用开关打开页面内隐藏的textarea进行调试,感觉很像给一台服务器接上了终端,然后使用shell可以做任何事情 函数可以在这里重新定义,可以任意操作界面中的任何元素,调用任何对象的任何函数,输出任何你需要的运行时刻值。
   5. 使用异常(exception)和断言(assert)
      使用try{}catch(e){}结构不光可以屏蔽出错信息,让界面更友好。我们的程序可以使用异常、抛出异常来构建一种更好的出错处理机制。
      有这样一个故事,我在使用string.localeCompare函数时随手写了这样的代码:
      var iRe = str1.localeCompare(str2);
      switch(iRe){
      0: return ....
      1: return ....
      -1:return ....
      defalut:throw "error:localeCompare return other value"
      }
      写完就忘了,没想到我的同事在linux下使用firefox时,异常被抛出了,然后我们得知:linux firefox下localeCompare返回的不只是0/1/-1,而是返回一个具体值.
      这个异常抛出有效的检测出了代码的不完美。

      firefox下的异常dump后能得到较为详细的调用栈信息,这一点非常好。IE的异常信息没有这么详细。

      异常和断言也可以结合成为一个非常有效的调试工具。
      断言(assert)是在其他语言中的一种很有效的调试工具,常常以这种形式出现:
      assert( <条件> ) ;
      在程序处于debug状态,当条件为假时,系统中止运行并报告这个断言。由于断言是我们自己定义的,所以我们可以很容易的判断出出错的地方,进而找到bug所在。
      javascript语言没有提供宏,也没有提供assert,我们可以这样模拟
      if(_is_debug) assert = function(expression , strLable){
          if( !expression ) throw Error(strLable);
      }
      else assert = function(){};//_is_debug是一个全局变量
      这样可以实现在发生"不可能的事情"的时候,让程序在调试模式下抛出异常,在发布版本中不作理会。

      可以这样输出当前栈的调用信息,弥补刚才提到的IE中异常对象没有栈信息的缺陷:
      function callStackInfo(){
      var s="",line="";
      var cer=arguments.callee.caller;
      while(cer){
      var sf=cer.toString();
      s+=line+sf.substring(sf.indexOf('function'),sf.indexOf('{'))+"\n";
      line=".."+line;
      cer=cer.caller;
      }
      return s;
      }

       本文只就javascript在web开发,特别是在ajax方面的开发做了一些讨论,主要在于管窥如何更好的使用“纯javascript”。web开发还有很多其他方面,比如xml和Dom等实际上和javascript息息相关,但是本文没有涉及,还请见谅。欢迎各位朋友就我的讨论多提意见。

 

转:http://bbs.chinaunix.net/viewthread.php?tid=690254&extra=page%3D1%26filter%3Ddigest


阅读全文(1359) | 评论:0 | 复制链接

字符,字节和编码(2006-10-22 17:28:00)

字符,字节和编码

[原创文章,转载请保留或注明出处:http://www.regexlab.com/zh/encoding.htm]

级别:中级

摘要:本文介绍了字符与编码的发展过程,相关概念的正确理解。举例说明了一些实际应用中,编码的实现方法。然后,本文讲述了通常对字符与编码的几种误解,由于这些误解而导致乱码产生的原因,以及消除乱码的办法。本文的内容涵盖了“中文问题”,“乱码问题”。

掌握编码问题的关键是正确地理解相关概念,编码所涉及的技术其实是很简单的。因此,阅读本文时需要慢读多想,多思考。

引言

“字符与编码”是一个被经常讨论的话题。即使这样,时常出现的乱码仍然困扰着大家。虽然我们有很多的办法可以用来消除乱码,但我们并不一定理解这些办法的内在原理。而有的乱码产生的原因,实际上由于底层代码本身有问题所导致的。因此,不仅是初学者会对字符编码感到模糊,有的底层开发人员同样对字符编码缺乏准确的理解。

1. 编码问题的由来,相关概念的理解

1.1 字符与编码的发展

从计算机对多国语言的支持角度看,大致可以分为三个阶段:

  系统内码 说明 系统
阶段一 ASCII 计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示。 英文 DOS
阶段二 ANSI编码
(本地化)
为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。

不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。

不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。
中文 DOS,中文 Windows 95/98,日文 Windows 95/98
阶段三 UNICODE
(国际化)
为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。 Windows NT/2000/XP,Linux,Java

字符串在内存中的存放方法:

在 ASCII 阶段,单字节字符串使用一个字节存放一个字符(SBCS)。比如,"Bob123" 在内存中为:

42 6F 62 31 32 33 00
B o b 1 2 3 \0

在使用 ANSI 编码支持多种语言阶段,每个字符使用一个字节或多个字节来表示(MBCS),因此,这种方式存放的字符也被称作多字节字符。比如,"中文123" 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节:

D6 D0 CE C4 31 32 33 00
1 2 3 \0

在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 "中文123" 在 Windows 2000 下,内存中实际存放的是 5 个序号:

2D 4E 87 65 31 00 32 00 33 00 00 00      ← 在 x86 CPU 中,低字节在前
1 2 3 \0  

一共占 10 个字节。

1.2 字符,字节,字符串

理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:

  概念描述 举例
字符 人们使用的记号,抽象意义上的一个符号。 '1', '中', 'a', '$', '¥', ……
字节 计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。 0x01, 0x45, 0xFA, ……
ANSI
字符串
在内存中,如果“字符”是以 ANSI 编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为 ANSI 字符串或者多字节字符串 "中文123"
(占7字节)
UNICODE
字符串
在内存中,如果“字符”是以在 UNICODE 中的序号存在的,那么我们称这种字符串为 UNICODE 字符串或者宽字节字符串 L"中文123"
(占10字节)

由于不同 ANSI 编码所规定的标准是不相同的,因此,对于一个给定的多字节字符串,我们必须知道它采用的是哪一种编码规则,才能够知道它包含了哪些“字符”。而对于 UNICODE 字符串来说,不管在什么环境下,它所代表的“字符”内容总是不变的。

1.3 字符集与编码

各个国家和地区所制定的不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”。比如:汉字标准(GB2312)中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义:

  1. 使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫做“字符集”。
  2. 规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”。

各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。因此,平常我们所说的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。

UNICODE 字符集”包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。

1.4 常用的编码简介

简单介绍一下常用的编码规则,为后边的章节做一个准备。在这里,我们根据编码规则的特点,把所有的编码分成三类:

分类 编码标准 说明
单字节字符编码 ISO-8859-1 最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 "ÖÐ"。

反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。
ANSI 编码 GB2312,
BIG5,
Shift_JIS,
ISO-8859-2 ……
把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。

反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 '中' 字。

“ANSI 编码”的特点:
1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。
2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。
UNICODE 编码 UTF-8,
UTF-16, UnicodeBig ……
与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。

与“ANSI 编码”不同的是:
1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。
2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。

我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节,我们只需要知道“编码”的概念就是把“字符”转化成“字节”就可以了。对于“UNICODE 编码”,由于它们是可以通过计算得到的,因此,在特殊的场合,我们可以去了解某一种“UNICODE 编码”是怎样的规则。

2. 字符与编码在程序中的实现

2.1 程序中的字符与字节

在 C++ 和 Java 中,用来代表“字符”和“字节”的数据类型,以及进行编码的方法:

类型或操作 C++ Java
字符 wchar_t char
字节 char byte
ANSI 字符串 char[] byte[]
UNICODE 字符串 wchar_t[] String
字节串→字符串 mbstowcs(), MultiByteToWideChar() string = new String(bytes, "encoding")
字符串→字节串 wcstombs(), WideCharToMultiByte() bytes = string.getBytes("encoding")

以上需要注意几点:

  1. Java 中的 char 代表一个“UNICODE 字符(宽字节字符)”,而 C++ 中的 char 代表一个字节。
  2. MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。
2.2 C++ 中相关实现方法

声明一段字符串常量:

// ANSI 字符串,内容长度 7 字节
char
     sz[20] = "中文123";

// UNICODE 字符串,内容长度 5 个 wchar_t(10 字节)
wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033";

UNICODE 字符串的 I/O 操作,字符与字节的转换操作:

// 运行时设定当前 ANSI 编码,VC 格式
setlocale(LC_ALL, ".936");

// GCC 中格式
setlocale(LC_ALL, "zh_CN.GBK");

// Visual C++ 中使用小写 %s,按照 setlocale 指定编码输出到文件
// GCC 中使用大写 %S

fwprintf(fp, L"%s\n", wsz);

// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节
wcstombs(sz, wsz, 20);
// 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串
mbstowcs(wsz, sz, 20);

在 Visual C++ 中,UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符,则需要使用 #pragma setlocale,告诉编译器源程序使用的编码:

// 如果源程序的编码与当前默认 ANSI 编码不一致,
// 则需要此行,编译时用来指明当前源程序使用的编码

#pragma setlocale
(".936")

// UNICODE 字符串常量,内容长度 10 字节
wchar_t wsz[20] = L"中文123";

以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的作用是不同的,#pragma setlocale 在编译时起作用,setlocale() 在运行时起作用。

2.3 Java 中相关实现方法

字符串类 String 中的内容是 UNICODE 字符串:

// Java 代码,直接写中文
String
string = "中文123";

// 得到长度为 5,因为是 5 个字符
System.out.println(string.length());

字符串 I/O 操作,字符与字节转换操作。在 Java 包 java.io.* 中,以“Stream”结尾的类一般是用来操作“字节串”的类,以“Reader”,“Writer”结尾的类一般是用来操作“字符串”的类。

// 字符串与字节串间相互转化

// 按照 GB2312 得到字节(得到多字节字符串)

byte
[] bytes = string.getBytes("GB2312");

// 从字节按照 GB2312 得到 UNICODE 字符串
string = new String(bytes, "GB2312");

// 要将 String 按照某种编码写入文本文件,有两种方法:

// 第一种办法:用 Stream 类写入已经按照指定编码转化好的字节串

OutputStream os = new FileOutputStream("1.txt");
os.write(bytes);
os.close();

// 第二种办法:构造指定编码的 Writer 来写入字符串
Writer ow = new OutputStreamWriter(new FileOutputStream("2.txt"), "GB2312");
ow.write(string);
ow.close();

/* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */

如果 java 的源程序编码与当前默认 ANSI 编码不符,则在编译的时候,需要指明一下源程序的编码。比如:

E:\>javac -encoding BIG5 Hello.java

以上需要注意区分源程序的编码与 I/O 操作的编码,前者是在编译时起作用,后者是在运行时起作用。

3. 几种误解,以及乱码产生的原因和解决办法

3.1 容易产生的误解
  对编码的误解
误解一 在将“字节串”转化成“UNICODE 字符串”时,比如在读取文本文件时,或者通过网络传输文本时,容易将“字节串”简单地作为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。

而实际上,在非英文的环境中,应该将“字节串”作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能“多个字节”才能得到“一个字符”。

通常,一直在英文环境下做开发的程序员们,容易有这种误解。
误解二 在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维:“字符串的编码”。

当 UNICODE 被支持后,Java 中的 String 是以字符的“序号”来存储的,不是以“某种编码的字节”来存储的,因此已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时,或者,将一个“字节串”当成一个 ANSI 字符串时,才有编码的概念。

不少的人都有这个误解。

第一种误解,往往是导致乱码产生的原因。第二种误解,往往导致本来容易纠正的乱码问题变得更复杂。

在这里,我们可以看到,其中所讲的“误解一”,即采用每“一个字节”就是“一个字符”的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作,得到原始的“字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, "GB2312"),来得到正确的“UNICODE 字符串”。

3.2 非 UNICODE 程序在不同语言环境间移植时的乱码

非 UNICODE 程序中的字符串,都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同,将会导致 ANSI 字符串的显示失败。

比如,在日文环境下开发的非 UNICODE 的日文程序界面,拿到中文环境下运行时,界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串,那么当在中文环境下运行时,界面上将可以显示正常的日文。

由于客观原因,有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件,这时我们可以采用一些工具,比如,南极星,AppLocale 等,暂时的模拟不同的语言环境。

3.3 网页提交字符串

当页面中的表单提交字符串时,首先把字符串按照当前页面的编码,转化成字节串。然后再将每个字节转化成 "%XX" 的格式提交到 Web 服务器。比如,一个编码为 GB2312 的页面,提交 "中" 这个字符串时,提交给服务器的内容为 "%D6%D0"。

在服务器端,Web 服务器把收到的 "%D6%D0" 转化成 [0xD6, 0xD0] 两个字节,然后再根据 GB2312 编码规则得到 "中" 字。

在 Tomcat 服务器中,request.getParameter() 得到乱码时,常常是因为前面提到的“误解一”造成的。默认情况下,当提交 "%D6%D0" 给 Tomcat 服务器时,request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符,而不是返回一个 "中" 字符。因此,我们需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字节串,再用 string = new String(bytes, "GB2312") 重新得到正确的字符串 "中"。

3.4 从数据库读取字符串

通过数据库客户端(比如 ODBC 或 JDBC)从数据库服务器中读取字符串时,客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时,客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。

如果从数据库读取字符串时得到乱码,而数据库中存放的数据又是正确的,那么往往还是因为前面提到的“误解一”造成的。解决的办法还是通过 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法,重新得到原始的字节串,再重新使用正确的编码转化成字符串。

3.5 电子邮件中的字符串

当一段 Text 或者 HTML 通过电子邮件传送时,发送的内容首先通过一种指定的字符编码转化成“字节串”,然后再把“字节串”通过一种指定的传输编码(Content-Transfer-Encoding)进行转化得到另一串“字节串”。比如,打开一封电子邮件源代码,可以看到类似的内容:

Content-Type: text/plain;
        charset="gb2312"
Content-Transfer-Encoding: base64

sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==

最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 两种。在对二进制文件或者中文文本进行转化时,Base64 得到的“字节串”比 Quoted-Printable 更短。在对英文文本进行转化时,Quoted-Printable 得到的“字节串”比 Base64 更短。

邮件的标题,用了一种更简短的格式来标注“字符编码”和“传输编码”。比如,标题内容为 "中",则在邮件源代码中表示为:

// 正确的标题格式
Subject: =?GB2312?B?1tA=?=

其中,

  • 第一个“=?”与“?”中间的部分指定了字符编码,在这个例子中指定的是 GB2312。
  • “?”与“?”中间的“B”代表 Base64。如果是“Q”则代表 Quoted-Printable。
  • 最后“?”与“?=”之间的部分,就是经过 GB2312 转化成字节串,再经过 Base64 转化后的标题内容。

如果“传输编码”改为 Quoted-Printable,同样,如果标题内容为 "中":

// 正确的标题格式
Subject: =?GB2312?Q?=D6=D0?=

如果阅读邮件时出现乱码,一般是因为“字符编码”或“传输编码”指定有误,或者是没有指定。比如,有的发邮件组件在发送邮件时,标题 "中":

// 错误的标题格式
Subject: =?ISO-8859-1?Q?=D6=D0?=

这样的表示,实际上是明确指明了标题为 [0x00D6, 0x00D0],即 "ÖÐ",而不是 "中"。

4. 几种错误理解的纠正

误解:“ISO-8859-1 是国际编码?”

非也。iso-8859-1 只是单字节字符集中最简单的一种,也就是“字节编号”与“UNICODE 字符编号”一致的那种编码规则。当我们要把一个“字节串”转化成“字符串”,而又不知道它是哪一种 ANSI 编码时,先暂时地把“每一个字节”作为“一个字符”进行转化,不会造成信息丢失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节串。

误解:“Java 中,怎样知道某个字符串的内码?”

Java 中,字符串类 java.lang.String 处理的是 UNICODE 字符串,不是 ANSI 字符串。我们只需要把字符串作为“抽象的符号的串”来看待。因此不存在字符串的内码的问题。


阅读全文(874) | 评论:0 | 复制链接

SQL安装时出错的解决办法(2006-10-9 17:04:00)

安装过程中出现“以前进行的程序创建了挂起的文件操作,运行安装程序前,必须重新启动”,请按照下面步骤解决: 

a、重启机器,再进行安装,如果发现还有该错误,请按下面步骤 
b、在开始->运行中输入regedit 
c、到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager 位置 
d、选择文件->倒出,保存 
e、在右边窗口右击PendingFileRenameOperations,选择删除,然后确认 
f、重启安装,问题解决 

如果还有同样问题,请检查其它注册表中是否有该值存在,如有请删掉。


引用地址:http://blog.programfan.com/trackback.asp?id=15568


阅读全文(1221) | 评论:0 | 复制链接

JavaScript函数小集锦(2006-10-9 17:02:00)

javascript提供了许多函数供开发人员使用,下面给出一个简单介绍,更详细的信息请参考Visual InterDev提供的在线帮助
  javascript函数一共可分为五类:
  ·常规函数
  ·数组函数
  ·日期函数
  ·数学函数
  ·字符串函数
  1.常规函数
  javascript常规函数包括以下9个函数:
  (1)alert函数:显示一个警告对话框,包括一个OK按钮。
  (2)confirm函数:显示一个确认对话框,包括OK、Cancel按钮。
  (3)escape函数:将字符转换成Unicode码。
  (4)eval函数:计算表达式的结果。
  (5)isNaN函数:测试是(true)否(false)不是一个数字。
  (6)parseFloat函数:将字符串转换成符点数字形式。
  (7)parseInt函数:将符串转换成整数数字形式(可指定几进制)。
  (8)prompt函数:显示一个输入对话框,提示等待用户输入。例如:
  <script language="javascript">
  <!--
  alert("输入错误");
  prompt("请输入您的姓名","姓名");
  confirm("确定否!");
  //-->
  </script>
  (9)unescape函数:解码由escape函数编码的字符。
  2.数组函数
  javascript数组函数包括以下4个函数:
  (1)join函数:转换并连接数组中的所有元素为一个字符串。例:
    function JoinDemo()
    {
     var a, b;
     a = new Array(0,1,2,3,4);
     b = a.join("-");//分隔符
     return(b);//返回的b=="0-1-2-3-4"
    }
  (2)langth函数:返回数组的长度。例:
    function LengthDemo()
    {
     var a, l;
     a = new Array(0,1,2,3,4);
     l = a.length;
     return(l);//l==5
    }
  (3)reverse函数:将数组元素顺序颠倒。例:
   function ReverseDemo()
   {
    var a, l;
    a = new Array(0,1,2,3,4);
    l = a.reverse();
    return(l);
   }
  (4)sort函数:将数组元素重新排序。例:
    function SortDemo()
    {
     var a, l;
     a = new Array("X" ,"y" ,"d", "Z", "v","m","r");
     l = a.sort();
     return(l);
    }
  3.日期函数
  javascript日期函数包括以下20个函数:
  (1)getDate函数:返回日期的“日”部分,值为1~31。例:
   function DateDemo()
   {
    var d, s = "Today's date is: ";
    d = new Date();
    s += (d.getMonth() + 1) + "/";
    s += d.getDate() + "/";
    s += d.getYear();
    return(s);
   }
  (2)getDay函数:返回星期几,值为0~6,其中0表示星期日,1表示星期一,...,6表示星期六。例:
   function DateDemo()
   {
    var d, day, x, s = "Today is: ";
    var x = new Array("Sunday", "Monday", "Tuesday");
    var x = x.concat("Wednesday","Thursday", "Friday");
    var x = x.concat("Saturday");
    d = new Date();
    day = d.getDay();
    return(s += x[day]);
   }
  (3)getHouse函数:返回日期的“小时”部分,值为0~23。例。
   function TimeDemo()
   {
    var d, s = "The current local time is: ";
    var c = ":";
    d = new Date();
    s += d.getHours() + c;
    s += d.getMinutes() + c;
    s += d.getSeconds() + c;
    s += d.getMilliseconds();
    return(s);
   }
  (4)getMinutes函数:返回日期的“分钟”部分,值为0~59。见上例。
  (5)getMonth函数:返回日期的“月”部分,值为0~11。其中0表示1月,2表示3月,...,11表示12月。见前面的例子。
  (6)getSeconds函数:返回日期的“秒”部分,值为0~59。见前面的例子。
  (7)getTime函数:返回系统时间。
   function GetTimeTest()
   {
    var d, s, t;
    var MinMilli = 1000 * 60;
    var HrMilli = MinMilli * 60;
    var DyMilli = HrMilli * 24;
    d = new Date();
    t = d.getTime();
    s = "It's been "
    s += Math.round(t / DyMilli) + " days since 1/1/70";
    return(s);
   }
  (8)getTimezoneOffset函数:返回此地区的时差(当地时间与GMT格林威治标准时间的地区时差),单位为分钟。
   function TZDemo()
   {
    var d, tz, s = "The current local time is ";
    d = new Date();
    tz = d.getTimezoneOffset();
    if (tz < 0)
    s += tz / 60 + " hours before GMT";
    else if (tz == 0)
    s += "GMT";
    else
    s += tz / 60 + " hours after GMT";
    return(s);
   }
  (9)getYear函数:返回日期的“年”部分。返回值以1900年为基数,例如1999年为99。前面有例子。
  (10)parse函数:返回从1970年1月1日零时整算起的毫秒数(当地时间)。
   function GetTimeTest(testdate)
   {
    var d, s, t;
    var MinMilli = 1000 * 60;
    var HrMilli = MinMilli * 60;
    var DyMilli = HrMilli * 24;
    d = new Date();
    t = Date.parse(testdate);
    s = "There are "
    s += Math.round(Math.abs(t / DyMilli)) + " days "
    s += "between " + testdate + " and 1/1/70";
    return(s);
   }
  (11)setDate函数:设定日期的“日”部分,值为0~31。
  (12)setHours函数:设定日期的“小时”部分,值为0~23。
  (13)setMinutes函数:设定日期的“分钟”部分,值为0~59。
  (14)setMonth函数:设定日期的“月”部分,值为0~11。其中0表示1月,...,11表示12月。
  (15)setSeconds函数:设定日期的“秒”部分,值为0~59。
  (16)setTime函数:设定时间。时间数值为1970年1月1日零时整算起的毫秒数。
  (17)setYear函数:设定日期的“年”部分。
  (18)toGMTString函数:转换日期成为字符串,为GMT格林威治标准时间。
  (19)setLocaleString函数:转换日期成为字符串,为当地时间。
  (20)UTC函数:返回从1970年1月1日零时整算起的毫秒数,以GMT格林威治标准时间计算。
  4.数学函数
  javascript数学函数其实就是Math对象,它包括属性和函数(或称方法)两部分。其中,属性主要有下列内容。
  Math.e:e(自然对数)、Math.LN2(2的自然对数)、Math.LN10(10的自然对数)、Math.LOG2E(e的对数,底数为2)、Math.LOG10E(e的对数,底数为10)、Math.PI(π)、Math.SQRT1_2(1/2的平方根值)、Math.SQRT2(2的平方根值)。
  函数有以下18个:
  (1)abs函数:即Math.abs(以下同),返回一个数字的绝对值。
  (2)acos函数:返回一个数字的反余弦值,结果为0~π弧度(radians)。
  (3)asin函数:返回一个数字的反正弦值,结果为-π/2~π/2弧度。
  (4)atan函数:返回一个数字的反正切值,结果为-π/2~π/2弧度。
  (5)atan2函数:返回一个坐标的极坐标角度值。
  (6)ceil函数:返回一个数字的最小整数值(大于或等于)。
  (7)cos函数:返回一个数字的余弦值,结果为-1~1。
  (8)exp函数:返回e(自然对数)的乘方值。
  (9)floor函数:返回一个数字的最大整数值(小于或等于)。
  (10)log函数:自然对数函数,返回一个数字的自然对数(e)值。
  (11)max函数:返回两个数的最大值。
  (12)min函数:返回两个数的最小值。
  (13)pow函数:返回一个数字的乘方值。
  (14)random函数:返回一个0~1的随机数值。
  (15)round函数:返回一个数字的四舍五入值,类型是整数。
  (16)sin函数:返回一个数字的正弦值,结果为-1~1。
  (17)sqrt函数:返回一个数字的平方根值。
  (18)tan函数:返回一个数字的正切值。
  5.字符串函数
  javascript字符串函数完成对字符串的字体大小、颜色、长度和查找等操作,共包括以下20个函数:
  (1)anchor函数:产生一个链接点(anchor)以作超级链接用。anchor函数设定<A NAME...>的链接点的名称,另一个函数link设定<A HREF=...>的URL地址。
  (2)big函数:将字体加到一号,与<BIG>...</BIG>标签结果相同。
  (3)blink函数:使字符串闪烁,与<BLINK>...</BLINK>标签结果相同。
  (4)bold函数:使字体加粗,与<B>...</B>标签结果相同。
  (5)charAt函数:返回字符串中指定的某个字符。
  (6)fixed函数:将字体设定为固定宽度字体,与<TT>...</TT>标签结果相同。
  (7)fontcolor函数:设定字体颜色,与<FONT COLOR=color>标签结果相同。
  (8)fontsize函数:设定字体大小,与<FONT SIZE=n>标签结果相同。
  (9)indexOf函数:返回字符串中第一个查找到的下标index,从左边开始查找。
  (10)italics函数:使字体成为斜体字,与<I>...</I>标签结果相同。
  (11)lastIndexOf函数:返回字符串中第一个查找到的下标index,从右边开始查找。
  (12)length函数:返回字符串的长度。(不用带括号)
  (13)link函数:产生一个超级链接,相当于设定<A HREF=...>的URL地址。
  (14)small函数:将字体减小一号,与<SMALL>...</SMALL>标签结果相同。
  (15)strike函数:在文本的中间加一条横线,与<STRIKE>...</STRIKE>标签结果相同。
  (16)sub函数:显示字符串为


引用地址:http://blog.programfan.com/trackback.asp?id=18423


阅读全文(1217) | 评论:0 | 复制链接

Java 编程语言中的口令屏蔽(2006-10-9 16:43:00)

Java 编程语言中的口令屏蔽《本文章引用:自:http://gceclub.sun.com.cn/staticcontent/html/2004-08-06/password.html


作者:Qusay H. Mahmoud
得到 Alan Sommerer 大力协助,2004 年 7 月

目前,对于 Java 命令行基于文本的输入/输出 API 的批评之一就是它缺乏对命令行输入口令屏蔽的支持。如果借助 AWT/Swing,这便不再成为问题,因为 AWT/Swing 提供了可以提供屏蔽口令的方法。

2002 年 9 月,我发表了本文的早期版本,其后便不断收到大量感谢信、建设性的意见和在应用程序中使用源代码的许可。本文:

  • 概述了口令屏蔽
  • 描述了用于口令屏蔽的 AWT/Swing 实用程序
  • 为命令行输入口令屏蔽问题提供独立于平台的解决方案
  • 为口令屏蔽提供一个改进的解决方案(可靠而安全)

口令屏蔽


登录屏幕和登录对话框使用口令屏蔽技术,这种技术要么在输入口令时隐藏口令,要么显示一个字符(比如星号'*')来代替用户输入的字符。例如,当您在一台 Windows 机器上进行登录时,一个登录对话框将会呈现在您眼前,其中的口令一栏使用星号作为屏蔽或回显字符。

Figure 1: UNIX login screen
图 1:UNIX 登录屏幕


如果操作系统是 UNIX,则登录屏幕中的口令栏不显示回显字符。它的做法很简单,就是什么都不显示,如图 1 所示。

AWT/Swing 中的口令屏蔽


如果您希望为您的应用程序提供图形化的登录对话框,您可以使用 AWT 的 TextField 类,该类是一个文本组件,允许编辑单行文本。为了屏蔽口令栏,要使用 setEchoChar 方法。例如,为了把回显字符设置为星号,您需要这样做:

TextField password = new TextField(8);
password.setEchoChar('*');

基于所使用字体的平均字符宽度,数字8指定了文本栏的宽度。您可以把回显字符设置为任何您喜欢的字符。注意,如果您把它设置为0,这意味着输入将会被回显,而不会被屏蔽。

Figure 2: JPasswordField with setEchoChar('#')
图 2:setEchoChar('#')后的JPasswordField


在 Swing 中,您可以使用 JPasswordField,它允许编辑单行文本,视图表明正在输入内容,但是不会显示原始字符。JPasswordField 类与和 setEchoChar 一起使用的 AWT 的 TextField 是源代码兼容的。如果您使用 JPasswordField, 默认的回显字符是星号 '*', 但是您可以将其修改为任何您选定的字符。此外,如果您把回显字符设置为 0,这意味着字符将在输入时显示出来,而不会被屏蔽。图 2 显示了一个 Swing 登录对话框,其中的回显字符被设置为 #,使用的是下面的代码片断:

JPasswordField password = new JPasswordField(8);
password.setEchoChar('#');


命令行输入屏蔽


和 AWT/Swing 不同,在 Java 中没有特殊的 API 可用于屏蔽命令行输入。这也是许多开发人员一直所要求的一项功能。如果您希望为命令行基于文本的 Java 应用程序以及服务器端 Java 应用程序提供一个登录屏幕,它就很有用。提供这种功能的一种方式就是使用 Java 本地接口(Java Native Interface ,JNI)。对于不了解 C/C++ 或者希望坚持 100% 纯 Java 代码的某些 Java 开发人员来说,这可能有一定难度。

这里我针对这个问题提出一个解决方案。在本文的早期版本中,所使用的是一个 UNIX 风格的登录屏幕,口令根本不在屏幕上回显。这样做的具体方法是,让一个单独的线程通过重写和打印口令提示命令行,尝试擦除回显到控制台的字符。大家仍然可以从论坛下载该篇文章中专用的代码和改进后的代码。

然而,大家最需要的功能之一是使用星号"*"替换回显的字符。因此,本文从为口令屏蔽提供一个简单的解决方案开始,接着给出改进后的、更加可靠和安全的代码。

简单的解决方案


这个解决方案使用一个单独的线程,在输入回显字符的时候擦除它们,然后使用星号代替它们。这是使用 EraserThread 类来完成的,如代码示例 1 所示

代码示例 1:EraserThread.java

import java.io.*;

class EraserThread implements Runnable {
   private boolean stop;
 
   /**
    *@param The prompt displayed to the user
    */
   public EraserThread(String prompt) {
       System.out.print(prompt);
   }

   /**
    * Begin masking...display asterisks (*)
    */
   public void run () {
      stop = true;
      while (stop) {
         System.out.print("\010*");
	 try {
	    Thread.currentThread().sleep(1);
         } catch(InterruptedException ie) {
            ie.printStackTrace();
         }
      }
   }

   /**
    * Instruct the thread to stop masking
    */
   public void stopMasking() {
      this.stop = false;
   }
}


注意: 这个解决方案广泛利用了线程,然而,如果机器负载很重,就不能确保 MaskingThread 能够足够经常地运行。请继续阅读本文的余下部分来了解代码的改进版本。

PasswordField 类使用了 EraserThread 类,这一点在代码示例 2 中体现出来了。这个类提示用户输入口令,而且 EraserThread 的一个实例尝试使用 "*" 屏蔽输入。注意,一开始将显示一个星号 (*)。

代码示例 2:PasswordField.java

public class PasswordField {

   /**
    *@param prompt The prompt to display to the user
    *@return The password as entered by the user
    */
   public static String readPassword (String prompt) {
      EraserThread et = new EraserThread(prompt);
      Thread mask = new Thread(et);
      mask.start();

      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      String password = "";

      try {
         password = in.readLine();
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
      // stop masking
      et.stopMasking();
      // return the password entered by the user
      return password;
   }
}


作为如何使用 PasswordField 类的一个例子,考虑应用程序 TestApp,如示例代码 3 所示。这个应用程序显示一条提示,并等待用户输入口令。当然,输入被屏蔽为星号(*)

代码示例 3:TestApp.java

class TestApp {
   public static void main(String argv[]) {
      String password = PasswordField.readPassword("Enter password: ");
      System.out.println("The password entered is: "+password);
   }
}


Figure 3: TestApp Sample Output
图 3:TestApp 示例输出


如果您在 Windows、MacOS 或 UNIX 操作系统上运行 TesApp, 您将会发现其输出与图 3 类似。此外还要注意,当您运行该应用程序时,会显示一个初始的星号。

使代码安全而可靠


上述的简单解决方案有一个主要缺陷:不应该使用字符串来存储诸如口令这类敏感信息!在本文的余下部分中,将会给出一个经过改进的解决方案。

然而,首先,MaskingThread 类能够从几处改进中获益:

  1. 为了确保跨线程的可见性,尤其是在多 CPU 的机器上,stop 字段应该被标记为 volatilevolatile 关键字指定同步线程使用该字段,这样编译器就不会对它进行任何优化;换句话说,应该从内存读取变量的值,而不应该在堆栈中保存任何拷贝。
  2. 为了确保屏蔽能够在系统高负荷运转时也能够出现,在调用持续期间,调用线程的优先权被设定为最大。返回时再恢复其原始的优先权。

代码示例 4 显示了修订后的 MaskingThread 类,修改的地方均以粗体形式突出显示。

代码示例 4:MaskingThread.java

import java.io.*;

/**
 * This class attempts to erase characters echoed to the console.
 */

class MaskingThread extends Thread {
   private volatile boolean stop;
   private char echochar = '*';

  /**
   *@param prompt The prompt displayed to the user
   */
   public MaskingThread(String prompt) {
      System.out.print(prompt);
   }

  /**
   * Begin masking until asked to stop.
   */
   public void run() {

      int priority = Thread.currentThread().getPriority();
      Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

      try {
         stop = true;
         while(stop) {
           System.out.print("\010" + echochar);
           try {
              // attempt masking at this rate
              Thread.currentThread().sleep(1);
           }catch (InterruptedException iex) {
              Thread.currentThread().interrupt();
              return;
           }
         }
      } finally { // restore the original priority
         Thread.currentThread().setPriority(priority);
      }
   }

  /**
   * Instruct the thread to stop masking.
   */
   public void stopMasking() {
      this.stop = false;
   }
}


尽管使用 Strings 收集和存储口令看起来似乎很合逻辑,它们并不适合存储诸如口令这样的敏感信息。这是因为 Strings 类型的对象是不可改变的——使用后不能重写或修改字符串的内容。应该使用一个 chars 数组作为代替。修订后的 PasswordField 如代码示例 5 所示,它是根据 Using Password-Based Encryption 改写而来

代码示例 5:PasswordField.java

import java.io.*;
import java.util.*;

/**
 * This class prompts the user for a password and attempts to mask input with "*"
 */

public class PasswordField {

  /**
   *@param input stream to be used (e.g. System.in)
   *@param prompt The prompt to display to the user.
   *@return The password as entered by the user.
   */

  public static final char[] getPassword(InputStream in, String prompt) 
    throws IOException {

      MaskingThread maskingthread = new MaskingThread(prompt);
      Thread thread = new Thread(maskingthread);
      thread.start();
	
      char[] lineBuffer;
      char[] buf;
      int i;

      buf = lineBuffer = new char[128];

      int room = buf.length;
      int offset = 0;
      int c;

      loop:   while (true) {
         switch (c = in.read()) {
            case -1:
            case '\n':
               break loop;

            case '\r':
               int c2 = in.read();
               if ((c2 != '\n') && (c2 != -1)) {
                  if (!(in instanceof PushbackInputStream)) {
                     in = new PushbackInputStream(in);
                  }
                  ((PushbackInputStream)in).unread(c2);
                } else {
                  break loop;
                }

                default:
                   if (--room < 0) {
                      buf = new char[offset + 128];
                      room = buf.length - offset - 1;
                      System.arraycopy(lineBuffer, 0, buf, 0, offset);
                      Arrays.fill(lineBuffer, ' ');
                      lineBuffer = buf;
                   }
                   buf[offset++] = (char) c;
                   break;
         }
      }
      maskingthread.stopMasking();
      if (offset == 0) {
         return null;
      }
      char[] ret = new char[offset];
      System.arraycopy(buf, 0, ret, 0, offset);
      Arrays.fill(buf, ' ');
      return ret;
   }
}


最后,PasswordApp 类如代码示例 6 所示,它只是一个用于测试修订后代码的测试应用程序。

代码示例 6:PasswordApp.java

import java.io.*;

public class PasswordApp {
   public static void main(String argv[]) {
      char password[] = null;
      try {
         password = PasswordField.getPassword(System.in, "Enter your password: ");
      } catch(IOException ioe) {
         ioe.printStackTrace();
      }
      if(password == null ) {
         System.out.println("No password entered");
      } else {
         System.out.println("The password entered is: "+String.valueOf(password));
      }
   }
}


Figure 4: PasswordApp Sample Output
图 4:PasswordApp 例子的输出


如果您在 Windows、MacOS 或 UNIX 操作系统上运行 TesApp, 您将会发现其输出与图 4 类似。

结束语


本文概览了在 Java 中进行口令屏蔽的方法,演示了在 AWT/Swing 应用程序中实现口令屏蔽是多么容易,并且为命令行基于文本的口令屏蔽提供了一个可重用的纯 Java 解决方案。

您可以在您的应用程序中自由地重用、改进和改编本文中的代码。您可以通过添加口令约束的方法来增强它。作为一次练习,您可能希望增强所给出的代码,以便口令的长度能够确定,而且口令中不允许使用某些字符,比如空格。

更多信息


致谢


特别感谢 Sun Microsystems 公司的工程师 Alan Sommerer 对于本文的贡献。


阅读全文(1143) | 评论:0 | 复制链接

java中的全屏幕模式(2006-10-9 16:21:00)

什么时候会用到全屏幕模式?
也许用到的机会很少,但JDK还是为我们提供了这个的功能。像许多软件中的打印预览功能,还有某些文本编辑器中为了获得更大的编辑画面,也用到了全屏幕模式,如果你有兴趣写一个像ACDSee这样的软件,使用全屏幕模式可以让用户看到更大的图片画面。

如何使用全屏幕模式?
关键是java.awt.*里面的两个与显示设备有关的类:GraphicsEnvironment和GraphicsDevice。

GraphicsEnvironment为Java应用程序提供了特定平台的 GraphicsDevice
对象和 Font 对象集合。这些GraphicsDevice可以是各种本机和远端机器的资源,如屏幕、打印机或者是Image Buffer,甚至是Graphics2D绘图方法的目标对象。

而GraphicsDevice就是指特定的图形环境了,如屏幕和打印设备等。这样,我们就可以用GraphicsDevice来操纵屏幕了。GraphicsDevice提供的setFullScreenWindow()方法就是设置全屏幕用的。

由于GraphicsEnvironment的构造器是受保护的(protected),我们不能直接构造一个
GraphicsEnvironment对象来获得GraphicsDevice对象。幸好它提供了getLocalGraphicsEnvironment()方法,用来获得一个GraphicsEnvironment实例:
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
有了GraphicsEnvironment可以调用getDefaultScreenDevice方法获得当前的屏幕设备了:
GraphicsDevice gd = ge.getDefaultScreenDevice();

自己动手体验一下
有了上面的简介,写一个实例来体验一下吧:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class FullScreenTest { 
   public static void main(String[] args){
       GraphicsEnvironment ge =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
       GraphicsDevice gd = ge.getDefaultScreenDevice(); 
       FullScreenWindow myWindow = new FullScreenWindow(); 
                   
         if ( gd.isFullScreenSupported() ) 
            gd.setFullScreenWindow(myWindow); 
         else 
            System.out.println("Unsupported full screen."); 
  }
}
class FullScreenWindow extends JWindow { 
    public FullScreenWindow() { 
        this.addMouseListener(new MouseAdapter() { 
            public void mousePressed(MouseEvent evt) { 
                  quit(); 
            } 
        }); 
    } 

    public void quit() { 
           this.dispose(); 
    } 

    public void paint(Graphics g) { 
        g.setFont(new Font("Arial",Font.BOLD,30)); 
        g.setColor(Color.RED); 
        g.drawString("这是全屏幕模式",100,100); 

    }

 }
引用的地址不小心给忘了。。。再找 没有找到<请作者看到后留言吧!!>

阅读全文(1064) | 评论:0 | 复制链接

在WinXP中装SQL SERVER企业版(2006-9-3 16:30:00)

在WinXP中装SQL SERVER企业版

一.在SQL服务器的安装盘中找到MSDE这个目录,并且点击setup.exe安装它,过程简单直接下一步就OK了。

  二. 重启系统WINDOWSXP,这下就可以看到SQL服务的图标出现了。

  三. 再拿出SQL服务器版的安装光盘,直接安装客户端工具(这个不要多说吧?最简单的方法就是直接点击光盘根目录下的autorun.exe)

  根据提示安装,自检过程中知道系统不是SERVER版,会提示只安装客户端工具。(哈哈,服务端我已有了)

  四. 打开企业管理器,试用SA用户连一下看看,是不是发现SA用户登陆失败?因为你还没有与信任SQL SERVER连接相关联。还好这个只要对系统注册表稍加修改就可以啦:

  在运行中输入regedit打开注册表编辑器,找到[HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\MSSQLSERVER\MSSQLSERVER],这个项里面有一个键值LoginMode,默认下,值是1,现在将值改为2,重启电脑。

阅读全文(1122) | 评论:2 | 复制链接

收藏_java的时间操作(2006-6-28 15:29:00)

java中的时间操作一

 文/Ray

java中的时间操作不外乎这四种情况:

1、获取当前时间

2、获取某个时间的某种格式

3、设置时间

4、时间的运算

好,下面就针对这三种情况,一个一个搞定。

 

一、获取当前时间

 

有两种方式可以获得,第一种,使用Date类。

j2SE的包里有两个Date类,一个是java.sql.Date,一个是java.util.Date

这里,要使用java.util.Date。获取当前时间的代码如下

Date date = new Date();

date.getTime();

还有一种方式,使用System.currentTimeMillis();

 

这两种方式获得的结果是一样的,都是得到一个当前的时间的long型的时间的毫秒值,这个值实际上是当前时间值与1970年一月一号零时零分零秒相差的毫秒数。

当前的时间得到了,但实际的应用中最后往往不是要用这个long型的东西,用户希望得到的往往是一个时间的字符串,比如“2006618号”,或“2006-06-18”,老外可能希望得到的是“06-18-2006”,诸如此类等等。这就是下一个要解决的问题

 

二、获取某个时间的某种格式

 

获取时间的格式,需要用到一个专门用于时间格式的类java.text.SimpleDateFormat

首先,定义一个SimpleDateFormat变量

SimpleDateFormat sdf = new SimpleDateFormat("",Locale.SIMPLIFIED_CHINESE);

这个构造函数的定义如下:

SimpleDateFormat(String pattern, Locale locale)

第一个参数pattern,我们后面再解释,这里我们使用一个"",第二个参数,是用来设置时区的,这里用到了java.util.Locale这个类,这个类了面定义了很多静态变量,直接拿过来用就OK,我们把时区设置为Locale.SIMPLIFIED_CHINESE,只看名字,这个静态变量的意义已经很清楚了。

 

接下来我们使用这个SimpleDateFormat把当前时间格式化为一个如下格式的时间字符串“XXXXXXXX_XXXXXX秒”,代码:

sdf.applyPattern("yyyy年MM月dd日_HH时mm分ss秒");

String timeStr = sdf.format(new Date());

 

获取时间格式的函数是format,这个函数的参数是java.util.Date对象,这个没有什么花头。

要说明一下的是这个pattern,所谓的模式。这里,yyyy,MM,dd等,这就是模式。

我们可以在SimpleDateFormat的构造函数中指定模式,比如

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",Locale.SIMPLIFIED_CHINESE);

也可以获取时间格式的时候使用applyPattern函数临时指定,上面的例子就是这样。

什么字符代表什么,这是j2se约定好的,设置模式的时候,我们可以使用约定好的字符加上任何我们想要的字符串。

j2se对字符所代表的模式的约定列表如下:

 

 

Letter Date or Time Component Presentation
G Era designator Text
y Year Year
M Month in year Month
w Week in year Number
W Week in month Number
D Day in year Number
d Day in month Number
F Day of week in month Number
E Day in week Text
a Am/pm marker Text 
H Hour in day (0-23) Number
k Hour in day (1-24) Number
K Hour in am/pm (0-11)  Number
h   Hour in am/pm (1-12)  Number
m   Minute in hour  Number
s   Second in minute  Number
S   Millisecond  Number  
z   Time zone   General time zone 
Z   Time zone  RFC 822 time zone 


阅读全文(1066) | 评论:0 | 复制链接

收藏_java的文件各种操作(2006-6-28 15:28:00)

java.io  
---------------------------------------------------------------  
 
/**  
 *  <p>Title:  文件的各种操作</p>  
 *  <p>Copyright:  Copyright  (c)  2004</p>  
 *  <p>Company:  广东  有限公司</p>  
 *  @author  网络信息部  庆丰  
 *  @version  1.0  
 */  
 
package  common;  
 
import  java.io.*;  
 
public  class  FileOperate  {  
   public  FileOperate()  {  
   }  
 
   /**  
     *  新建目录  
     *  @param  folderPath  String  如  c:/fqf  
     *  @return  boolean  
     */  
   public  void  newFolder(String  folderPath)  {  
       try  {  
           String  filePath  =  folderPath;  
           filePath  =  filePath.toString();  
           java.io.File  myFilePath  =  new  java.io.File(filePath);  
           if  (!myFilePath.exists())  {  
               myFilePath.mkdir();  
           }  
       }  
       catch  (Exception  e)  {  
           System.out.println("新建目录操作出错");  
           e.printStackTrace();  
       }  
   }  
 
   /**  
     *  新建文件  
     *  @param  filePathAndName  String  文件路径及名称  如c:/fqf.txt  
     *  @param  fileContent  String  文件内容  
     *  @return  boolean  
     */  
   public  void  newFile(String  filePathAndName,  String  fileContent)  {  
 
       try  {  
           String  filePath  =  filePathAndName;  
           filePath  =  filePath.toString();  
           File  myFilePath  =  new  File(filePath);  
           if  (!myFilePath.exists())  {  
               myFilePath.createNewFile();  
           }  
           FileWriter  resultFile  =  new  FileWriter(myFilePath);  
           PrintWriter  myFile  =  new  PrintWriter(resultFile);  
           String  strContent  =  fileContent;  
           myFile.println(strContent);  
           resultFile.close();  
 
       }  
       catch  (Exception  e)  {  
           System.out.println("新建目录操作出错");  
           e.printStackTrace();  
 
       }  
 
   }  
 
   /**  
     *  删除文件  
     *  @param  filePathAndName  String  文件路径及名称  如c:/fqf.txt  
     *  @param  fileContent  String  
     *  @return  boolean  
     */  
   public  void  delFile(String  filePathAndName)  {  
       try  {  
           String  filePath  =  filePathAndName;  
           filePath  =  filePath.toString();  
           java.io.File  myDelFile  =  new  java.io.File(filePath);  
           myDelFile.delete();  
 
       }  
       catch  (Exception  e)  {  
           System.out.println("删除文件操作出错");  
           e.printStackTrace();  
 
       }  
 
   }  
 
   /**  
     *  删除文件夹  
     *  @param  filePathAndName  String  文件夹路径及名称  如c:/fqf  
     *  @param  fileContent  String  
     *  @return  boolean  
     */  
   public  void  delFolder(String  folderPath)  {  
       try  {  
           delAllFile(folderPath);  //删除完里面所有内容  
           String  filePath  =  folderPath;  
           filePath  =  filePath.toString();  
           java.io.File  myFilePath  =  new  java.io.File(filePath);  
           myFilePath.delete();  //删除空文件夹  
 
       }  
       catch  (Exception  e)  {  
           System.out.println("删除文件夹操作出错");  
           e.printStackTrace();  
 
       }  
 
   }  
 
   /**  
     *  删除文件夹里面的所有文件  
     *  @param  path  String  文件夹路径  如  c:/fqf  
     */  
   public  void  delAllFile(String  path)  {  
       File  file  =  new  File(path);  
       if  (!file.exists())  {  
           return;  
       }  
       if  (!file.isDirectory())  {  
           return;  
       }  
       String[]  tempList  =  file.list();  
       File  temp  =  null;  
       for  (int  i  =  0;  i  <  tempList.length;  i++)  {  
           if  (path.endsWith(File.separator))  {  
               temp  =  new  File(path  +  tempList[i]);  
           }  
           else  {  
               temp  =  new  File(path  +  File.separator  +  tempList[i]);  
           }  
           if  (temp.isFile())  {  
               temp.delete();  
           }  
           if  (temp.isDirectory())  {  
               delAllFile(path+"/"+  tempList[i]);//先删除文件夹里面的文件  
               delFolder(path+"/"+  tempList[i]);//再删除空文件夹  
           }  
       }  
   }  
 
   /**  
     *  复制单个文件  
     *  @param  oldPath  String  原文件路径  如:c:/fqf.txt  
     *  @param  newPath  String  复制后路径  如:f:/fqf.txt  
     *  @return  boolean  
     */  
   public  void  copyFile(String  oldPath,  String  newPath)  {  
       try  {  
           int  bytesum  =  0;  
           int  byteread  =  0;  
           File  oldfile  =  new  File(oldPath);  
           if  (oldfile.exists())  {  //文件存在时  
               InputStream  inStream  =  new  FileInputStream(oldPath);  //读入原文件  
               FileOutputStream  fs  =  new  FileOutputStream(newPath);  
               byte[]  buffer  =  new  byte[1444];  
               int  length;  
               while  (  (byteread  =  inStream.read(buffer))  !=  -1)  {  
                   bytesum  +=  byteread;  //字节数  文件大小  
                   System.out.println(bytesum);  
                   fs.write(buffer,  0,  byteread);  
               }  
               inStream.close();  
           }  
       }  
       catch  (Exception  e)  {  
           System.out.println("复制单个文件操作出错");  
           e.printStackTrace();  
 
       }  
 
   }  
 
   /**  
     *  复制整个文件夹内容  
     *  @param  oldPath  String  原文件路径  如:c:/fqf  
     *  @param  newPath  String  复制后路径  如:f:/fqf/ff  
     *  @return  boolean  
     */  
   public  void  copyFolder(String  oldPath,  String  newPath)  {  
 
       try  {  
           (new  File(newPath)).mkdirs();  //如果文件夹不存在  则建立新文件夹  
           File  a=new  File(oldPath);  
           String[]  file=a.list();  
           File  temp=null;  
           for  (int  i  =  0;  i  <  file.length;  i++)  {  
               if(oldPath.endsWith(File.separator)){  
                   temp=new  File(oldPath+file[i]);  
               }  
               else{  
                   temp=new  File(oldPath+File.separator+file[i]);  
               }  
 
               if(temp.isFile()){  
                   FileInputStream  input  =  new  FileInputStream(temp);  
                   FileOutputStream  output  =  new  FileOutputStream(newPath  +  "/"  +  
                           (temp.getName()).toString());  
                   byte[]  b  =  new  byte[1024  *  5];  
                   int  len;  
                   while  (  (len  =  input.read(b))  !=  -1)  {  
                       output.write(b,  0,  len);  
                   }  
                   output.flush();  
                   output.close();  
                   input.close();  
               }  
               if(temp.isDirectory()){//如果是子文件夹  
                   copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);  
               }  
           }  
       }  
       catch  (Exception  e)  {  
           System.out.println("复制整个文件夹内容操作出错");  
           e.printStackTrace();  
 
       }  
 
   }  
 
   /**  
     *  移动文件到指定目录  
     *  @param  oldPath  String  如:c:/fqf.txt  
     *  @param  newPath  String  如:d:/fqf.txt  
     */  
   public  void  moveFile(String  oldPath,  String  newPath)  {  
       copyFile(oldPath,  newPath);  
       delFile(oldPath);  
 
   }  
 
   /**  
     *  移动文件到指定目录  
     *  @param  oldPath  String  如:c:/fqf.txt  
     *  @param  newPath  String  如:d:/fqf.txt  
     */  
   public  void  moveFolder(String  oldPath,  String  newPath)  {  
       copyFolder(oldPath,  newPath);  
       delFolder(oldPath);  
 
   }  
}

阅读全文(1098) | 评论:0 | 复制链接

Tomcat-Web.xml<解释>(2006-5-21 9:58:00)

经常看到jsp的初学者问tomcat下如何配置jsp、servlet和bean的问题,于是总结了一下如何tomcat下配置jsp、servlet和ben,希望对那些初学者有所帮助。

第一步:下载j2sdk和tomcat:到sun官方站点(http://java.sun.com/j2se/1.4.2/download.html)下载j2sdk,注意下载版本为Windows Offline Installation的SDK,同时最好下载J2SE 1.4.2 Documentation,然后到tomcat官方站点(http://www.apache.org/dist/jakarta/tomcat-4/)下载tomcat(下载最新4.1.x版本的tomcat);

第二步:安装和配置你的j2sdk和tomcat:执行j2sdk和tomcat的安装程序,然后按默认设置进行安装即可。
1.安装j2sdk以后,需要配置一下环境变量,在我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量(假定你的j2sdk安装在c:\j2sdk1.4.2):
JAVA_HOME=c:\j2sdk1.4.2
classpath=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;(.;一定不能少,因为它代表当前路径)
path=%JAVA_HOME%\bin
接着可以写一个简单的java程序来测试J2SDK是否已安装成功:
public class Test{
public static void main(String args[]){
System.out.println("This is a test program.");
}
}
将上面的这段程序保存为文件名为Test.java的文件。
然后打开命令提示符窗口,cd到你的Test.java所在目录,然后键入下面的命令
javac Test.java
java Test
此时如果看到打印出来This is a test program.的话说明安装成功了,如果没有打印出这句话,你需要仔细检查一下你的配置情况。

2.安装Tomcat后,在我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量(假定你的tomcat安装在c:\tomcat):
CATALINA_HOME=c:\tomcat;
CATALINA_BASE=c:\tomcat;
然后修改环境变量中的classpath,把tomat安装目录下的common\lib下的servlet.jar追加到classpath中去,修改后的classpath如下:
classpath=.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;%CATALINA_HOME%\common\lib\servlet.jar;
接着可以启动tomcat,在IE中访问http://localhost:8080
如果看到tomcat的欢迎页面的话说明安装成功了。

第三步:建立自己的jsp app目录
1.到Tomcat的安装目录的webapps目录,可以看到ROOT,examples, tomcat-docs之类Tomcat自带的的目录;
2.在webapps目录下新建一个目录,起名叫myapp;
3.myapp下新建一个目录WEB-INF,注意,目录名称是区分大小写的;
4.WEB-INF下新建一个文件web.xml,内容如下:
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<display-name>My Web Application</display-name>
<description>
A application for test.
</description>
</web-app>
5.在myapp下新建一个测试的jsp页面,文件名为index.jsp,文件内容如下:
<html><body><center>
Now time is: <%=new java.util.Date()%>
</center></body></html>
6.重启Tomcat
7.打开浏览器,输入http://localhost:8080/myapp/index.jsp 看到当前时间的话说明就成功了。

第四步:建立自己的Servlet:
1.用你最熟悉的编辑器(建议使用有语法检查的java ide)新建一个servlet程序,文件名为Test.java,文件内容如下:
package test;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Test extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out=response.getWriter();
out.println("<html><body><h1>This is a servlet test.</h1></body></html>");
out.flush();
}
}
2 .编译
将Test.java放在c:\test下,使用如下命令编译:
C:\Test>javac Test.java
然后在c:\Test下会产生一个编译后的servlet文件:Test.class
3 .将结构test\Test.class剪切到%CATALINA_HOME%\webapps\myapp\WEB-INF\classes下,也就是剪切那个test目录到classes目录下,如果classes目录不存在,就新建一个。 现在webapps\myapp\WEB-INF\classes下有test\Test.class的文件目录结构
4 .修改webapps\myapp\WEB-INF\web.xml,添加servlet和servlet-mapping
编辑后的web.xml如下所示,红色为添加的内容:
<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<display-name>My Web Application</display-name>
<description>
A application for test.
</description>
<servlet>
<servlet-name>Test</servlet-name>
<display-name>Test</display-name>
<description>A test Servlet</description>
<servlet-class>test.Test</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/Test</url-pattern>
</servlet-mapping>
</web-app>
这段话中的servlet这一段声明了你要调用的Servlet,而servlet-mapping则是将声明的servlet“映射”到地址/Test上
5 .好了,启动Tomcat,启动浏览器,输入http://localhost:8080/myapp/Test 如果看到输出This is a servlet test.就说明编写的servlet成功了。
注意:修改了web.xml以及新加了class,都要重启Tomcat

第四步:建立自己的Bean:
1.用你最熟悉的编辑器(建议使用有语法检查的java ide)新建一个java程序,文件名为TestBean.java,文件内容如下:
package test;
public class TestBean{
private String name = null;
public TestBean(String strName_p){
this.name=strName_p;
}
public void setName(String strName_p){
this.name=strName_p;
}
public String getName(){
return this.name;
}
}
2 .编译
将TestBean.java放在c:\test下,使用如下命令编译:
C:\Test>javac TestBean.java
然后在c:\Test下会产生一个编译后的bean文件:TestBean.class
3 .将TestBean.class文件剪切到 %CATALINA_HOME%\webapps\myapp\WEB-INF\classes\test下,
4 .新建一个TestBean.jsp文件,文件内容为:
<%@ page import="test.TestBean" %>
<html><body><center>
<%
TestBean testBean=new TestBean("This is a test java bean.");
%>
Java bean name is: <%=testBean.getName()%>
</center></body></html>
5 .好了,重启Tomcat,启动浏览器,输入http://localhost:8080/myapp/TestBean.jsp 如果看到输出Java bean name is: This is a test java bean.就说明编写的Bean成功了。


阅读全文(4897) | 评论:3 | 复制链接