<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-17  評論-6  文章-1  trackbacks-0
      2005年12月14日
    find   basedir   -name   CVS   |xargs   rm   -rf  
    posted @ 2007-04-17 14:32 小鐵匠 閱讀(323) | 評論 (0)編輯 收藏


    ??? public void doFilter(ServletRequest request, ServletResponse response,
    ??????????? FilterChain chain) throws IOException, ServletException {

    ??????? HttpServletRequest req = (HttpServletRequest) request;

    ??????? int length = req.getContentLength();
    ??????? if (length > 0) {
    ??????????? BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(req,length);

    ??????????? InputStream is = bufferedRequest.getInputStream();
    ??????????? byte[] content = new byte[length];
    ???????????
    ??????????? int pad = 0;
    ??????????? while(pad < length){
    ??????????????? pad += is.read(content, pad, length);
    ??????????? }

    ????????????request = bufferedRequest;
    ??????? }
    ??????? chain.doFilter(request, response);??????
    ??? }


    BufferedRequestWrapper .java

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;

    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;

    public class BufferedRequestWrapper extends HttpServletRequestWrapper {

    ??? ByteArrayInputStream bais;

    ??? BufferedServletInputStream bsis;

    ??? byte[] buffer;

    ??? public BufferedRequestWrapper(HttpServletRequest req,int length) throws IOException {
    ??????? super(req);
    ??????? // Read InputStream and store its content in a buffer.
    ??????? InputStream is = req.getInputStream();
    ??????? buffer = new byte[length];

    ??????? int pad = 0;
    ??????? while(pad < length){
    ??????????? pad += is.read(buffer, pad, length);
    ??????? }
    ??? }

    ??? public ServletInputStream getInputStream() {
    ??????? try {
    ??????????? // Generate a new InputStream by stored buffer
    ??????????? bais = new ByteArrayInputStream(buffer);
    ??????????? // Istantiate a subclass of ServletInputStream
    ??????????? // (Only ServletInputStream or subclasses of it are accepted by the
    ??????????? // servlet engine!)
    ??????????? bsis = new BufferedServletInputStream(bais);
    ??????? } catch (Exception ex) {
    ??????????? ex.printStackTrace();
    ??????? } finally {
    ??????? }
    ??????? return bsis;
    ??? }

    }




    BufferedServletInputStream .java

    import java.io.*;
    import javax.servlet.ServletInputStream;

    /*
    ?Subclass of ServletInputStream needed by the servlet engine.
    ?All inputStream methods are wrapped and are delegated to
    ?the ByteArrayInputStream (obtained as constructor parameter)!
    ?*/
    public class BufferedServletInputStream extends ServletInputStream {

    ??? ByteArrayInputStream bais;

    ??? public BufferedServletInputStream(ByteArrayInputStream bais) {
    ??????? this.bais = bais;
    ??? }

    ??? public int available() {
    ??????? return bais.available();
    ??? }

    ??? public int read() {
    ??????? return bais.read();
    ??? }

    ??? public int read(byte[] buf, int off, int len) {
    ??????? return bais.read(buf, off, len);
    ??? }

    }

    posted @ 2006-10-25 17:01 小鐵匠 閱讀(2740) | 評論 (1)編輯 收藏
    $ gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11
    will compile and link a program called x11fred using the version of the library libX11 found in the
    /usr/openwin/lib directory.
    posted @ 2006-07-10 14:54 小鐵匠 閱讀(422) | 評論 (0)編輯 收藏

    有發送人名稱中文支持,支持bytes格式附件,附件中文支持

    ? public static boolean send(String fromName, String fromAddr, String to, String subject, String
    ???????????????????????????? body, String fileName, byte[] file) throws
    ????? Exception {
    ??????? //發送人名稱,用base64編碼,再加上特殊標志
    ??????? fromName = "=?GB2312?B?" + new String(base64.encode((fromName).getBytes()))? + "?=";
    ??? Properties props = new Properties();
    ??? Session session = Session.getInstance(props, null);
    ??? props.put("mail.smtp.host", Constants.mailhost);
    ??? props.put("mail.smtp.auth", "false");?
    ??? Message msg = new MimeMessage(session);
    ????? msg.setFrom(new InternetAddress(fromAddr,fromName));
    //后面的BodyPart將加入到此處創建的Multipart中
    ??? Multipart mp = new MimeMultipart();
    // Create the message part
    ??? BodyPart messageBodyPart = new MimeBodyPart();

    ??? // Fill the message
    ??? messageBodyPart.setText(body);

    ??? mp.addBodyPart(messageBodyPart);

    ????? /*發送附件*/
    ???? if (file != null && file.length > 0) {
    ?????? //利用枚舉器方便的遍歷集合
    ???????? MimeBodyPart mbp = new MimeBodyPart();?
    //???????? File fileTmp = null;
    ???????? //得到數據源
    //???????? FileDataSource fds = new FileDataSource(fileTmp);
    ???????? //得到附件本身并至入BodyPart
    ???????? mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(file,"application/octet-stream")));
    ???????? //得到文件名同樣至入BodyPart
    ???????? mbp.setFileName(MimeUtility.encodeWord(fileName,"GB2312",null));
    ???????? mp.addBodyPart(mbp);
    ???? }
    ???
    ??? //Multipart加入到信件
    ??? msg.setContent(mp);

    ??? msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
    ??? msg.setSubject(subject);

    ??? msg.setHeader("X-Mailer", "personal Email Sender");
    ??? msg.setSentDate(new Date());

    ??? Transport transport = session.getTransport("smtp");

    ??? //添加認證信息
    ??? transport.connect(Constants.mailhost, Constants.user, Constants.pwd);
    ??? transport.sendMessage(msg, msg.getRecipients(Message.RecipientType.TO));
    ??? transport.close();
    ??? return true;
    ? }




    import java.io.*;
    import javax.activation.*;

    public class ByteArrayDataSource implements DataSource {
    ??? /** * Data to write. */
    ??? private byte[] _data;

    ??? /** * Content-Type. */
    ??? private String _type;

    ??? /* Create a datasource from an input stream */
    ??? public ByteArrayDataSource(InputStream is, String type) {
    ??????? _type = type;
    ??????? try {
    ??????????? ByteArrayOutputStream os = new ByteArrayOutputStream();
    ??????????? int ch;

    ??????????? // XXX : must be made more efficient by
    ??????????? // doing buffered reads, rather than one byte reads
    ??????????? while ((ch = is.read()) != -1)
    ??????????????? os.write(ch);
    ??????????? _data = os.toByteArray();
    ??????? } catch (IOException ioe) {
    ??????? }
    ??? }

    ??? /* Create a datasource from a byte array */
    ??? public ByteArrayDataSource(byte[] data, String type) {
    ??????? _data = data;
    ??????? _type = type;
    ??? }

    ??? /* Create a datasource from a String */
    ??? public ByteArrayDataSource(String data, String type) {
    ??????? try {
    ??????????? // Assumption that the string contains only ascii
    ??????????? // characters ! Else just pass in a charset into this
    ??????????? // constructor and use it in getBytes()
    ??????????? _data = data.getBytes("iso-8859-1");
    ??????? } catch (UnsupportedEncodingException uee) {
    ??????? }
    ??????? _type = type;
    ??? }

    ??? public InputStream getInputStream() throws IOException {
    ??????? if (_data == null)
    ??????????? throw new IOException("no data");
    ??????? return new ByteArrayInputStream(_data);
    ??? }

    ??? public OutputStream getOutputStream() throws IOException {
    ??????? throw new IOException("cannot do this");
    ??? }

    ??? public String getContentType() {
    ??????? return _type;
    ??? }

    ??? public String getName() {
    ??????? return "dummy";
    ??? }
    }

    posted @ 2006-05-10 18:02 小鐵匠 閱讀(645) | 評論 (1)編輯 收藏
    C/C++頭文件一覽

    C、傳統 C++

    #include <assert.h>    //設定插入點
    #include <ctype.h>     //字符處理
    #include <errno.h>     //定義錯誤碼
    #include <float.h>     //浮點數處理
    #include <fstream.h>    //文件輸入/輸出
    #include <iomanip.h>    //參數化輸入/輸出
    #include <iostream.h>   //數據流輸入/輸出
    #include <limits.h>    //定義各種數據類型最值常量
    #include <locale.h>    //定義本地化函數
    #include <math.h>     //定義數學函數
    #include <stdio.h>     //定義輸入/輸出函數
    #include <stdlib.h>    //定義雜項函數及內存分配函數
    #include <string.h>    //字符串處理
    #include <strstrea.h>   //基于數組的輸入/輸出
    #include <time.h>     //定義關于時間的函數
    #include <wchar.h>     //寬字符處理及輸入/輸出
    #include <wctype.h>    //寬字符分類

    //////////////////////////////////////////////////////////////////////////

    標準 C++ (同上的不再注釋)

    #include <algorithm>    //STL 通用算法
    #include <bitset>     //STL 位集容器
    #include <cctype>
    #include <cerrno>
    #include <clocale>
    #include <cmath>
    #include <complex>     //復數類
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <ctime>
    #include <deque>      //STL 雙端隊列容器
    #include <exception>    //異常處理類
    #include <fstream>
    #include <functional>   //STL 定義運算函數(代替運算符)
    #include <limits>
    #include <list>      //STL 線性列表容器
    #include <map>       //STL 映射容器
    #include <iomanip>
    #include <ios>       //基本輸入/輸出支持
    #include <iosfwd>     //輸入/輸出系統使用的前置聲明
    #include <iostream>
    #include <istream>     //基本輸入流
    #include <ostream>     //基本輸出流
    #include <queue>      //STL 隊列容器
    #include <set>       //STL 集合容器
    #include <sstream>     //基于字符串的流
    #include <stack>      //STL 堆棧容器    
    #include <stdexcept>    //標準異常類
    #include <streambuf>    //底層輸入/輸出支持
    #include <string>     //字符串類
    #include <utility>     //STL 通用模板類
    #include <vector>     //STL 動態數組容器
    #include <cwchar>
    #include <cwctype>

    using namespace std;

    //////////////////////////////////////////////////////////////////////////

    C99 增加

    #include <complex.h>   //復數處理
    #include <fenv.h>    //浮點環境
    #include <inttypes.h>  //整數格式轉換
    #include <stdbool.h>   //布爾環境
    #include <stdint.h>   //整型環境
    #include <tgmath.h>   //通用類型數學宏

    posted @ 2006-03-09 16:55 小鐵匠 閱讀(534) | 評論 (0)編輯 收藏
    string.h中的函數

    @函數名稱:   strdup
    函數原型:   char *strdup(const char *s)
    函數功能:   字符串拷貝,目的空間由該函數分配
    函數返回:   指向拷貝后的字符串指針
    參數說明:   src-待拷貝的源字符串
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    #include <alloc.h>
    int main()
    {
      char *dup_str, *string="abcde";
      dup_str=strdup(string);
      printf("%s", dup_str);
      free(dup_str);
      return 0;
    }


    @函數名稱:   strcpy
    函數原型:   char* strcpy(char* str1,char* str2);
    函數功能:   把str2指向的字符串拷貝到str1中去
    函數返回:   返回str1,即指向str1的指針
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char string[10];
      char *str1="abcdefghi";
      strcpy(string,str1);
      printf("the string is:%s\n",string);
      return 0;
    }


    @函數名稱:   strncpy
    函數原型:   char *strncpy(char *dest, const char *src,int count)
    函數功能:   將字符串src中的count個字符拷貝到字符串dest中去
    函數返回:   指向dest的指針
    參數說明:   dest-目的字符串,src-源字符串,count-拷貝的字符個數
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char string[10];
      char *str1="abcdefghi";
      strncpy(string,str1,3);
      string[3]='\0';
      printf("%s",string);
      return 0;
    }


    @函數名稱:   strcat
    函數原型:   char* strcat(char * str1,char * str2);
    函數功能:   把字符串str2接到str1后面,str1最后的'\0'被取消
    函數返回:   str1
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>

    int main()
    {
      char buffer[80];

      strcpy(buffer,"Hello ");
      strcat(buffer,"world");
      printf("%s\n",buffer);
      return 0;
    }


    @函數名稱:   strncat
    函數原型:   char *strncat(char *dest, const char *src, size_t maxlen)
    函數功能:   將字符串src中前maxlen個字符連接到dest中
    函數返回:
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>

    char buffer[80];

    int main()
    {
      strcpy(buffer,"Hello ");
      strncat(buffer,"world",8);
      printf("%s\n",buffer);
      strncat(buffer,"*************",4);
      printf("%s\n",buffer);
      return 0;
    }


    @函數名稱:   strcmp
    函數原型:   int strcmp(char * str1,char * str2);
    函數功能:   比較兩個字符串str1,str2.
    函數返回:   str1<str2,返回負數; str1=str2,返回 0; str1>str2,返回正數.
    參數說明:
    所屬文件:   <string.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char *buf1="aaa", *buf2="bbb", *buf3="ccc";
      int ptr;
      ptr=strcmp(buf2, buf1);
      if(ptr>0)
        printf("buffer 2 is greater than buffer 1\n");
      else
        printf("buffer 2 is less than buffer 1\n");
      ptr=strcmp(buf2, buf3);
      if(ptr>0)
        printf("buffer 2 is greater than buffer 3\n");
      else
        printf("buffer 2 is less than buffer 3\n");
      return 0;
    }


    @函數名稱:   strncmp
    函數原型:   int strncmp(char *str1,char *str2,int count)
    函數功能:   對str1和str2中的前count個字符按字典順序比較
    函數返回:   小于0:str1<str2,等于0:str1=str2,大于0:str1>str2
    參數說明:   str1,str2-待比較的字符串,count-比較的長度
    所屬文件:   <string.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      int ptr;
      char *buf1="aaabbb",*buf2="bbbccc",*buf3="ccc";
      ptr=strncmp(buf2,buf1,3);
      if (ptr>0)
        printf("buffer 2 is greater than buffer 1");
      else
        printf("buffer 2 is less than buffer 1");
        ptr=strncmp(buf2,buf3,3);
      if (ptr>0)
        printf("buffer 2 is greater than buffer 3");
      else
        printf("buffer 2 is less than buffer 3");
      return(0);
    }


    @函數名稱:   strpbrk
    函數原型:   char *strpbrk(const char *s1, const char *s2)
    函數功能:   得到s1中第一個“同時也出現在s2中”字符的位置指針
    函數返回:   位置指針
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
    char *p="Find all vowels";

    while(p)
    {
      printf("%s\n",p);
      p=strpbrk(p+1,"aeiouAEIOU");
    }
    return 0;
    }


    @函數名稱:   strcspn
    函數原型:   int strcspn(const char *s1, const char *s2)
    函數功能:   統計s1中從頭開始直到第一個“來自s2中的字符”出現的長度
    函數返回:   長度
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>

    int main()
    {
      printf("%d\n",strcspn("abcbcadef","cba"));
      printf("%d\n",strcspn("xxxbcadef","cba"));
      printf("%d\n",strcspn("123456789","cba"));
      return 0;
    }


    @函數名稱:   strspn
    函數原型:   int strspn(const char *s1, const char *s2)
    函數功能:   統計s1中從頭開始直到第一個“不來自s2中的字符”出現的長度
    函數返回:   位置指針
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    #include <alloc.h>
    int main()
    {
      printf("%d\n",strspn("out to lunch","aeiou"));
      printf("%d\n",strspn("out to lunch","xyz"));
      return 0;
    }


    @函數名稱:   strchr
    函數原型:   char* strchr(char* str,char ch);
    函數功能:   找出str指向的字符串中第一次出現字符ch的位置
    函數返回:   返回指向該位置的指針,如找不到,則返回空指針
    參數說明:   str-待搜索的字符串,ch-查找的字符
    所屬文件:   <string.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char string[15];
      char *ptr, c='r';
      strcpy(string, "This is a string");
      ptr=strchr(string, c);
      if (ptr)
        printf("The character %c is at position: %d\n",c,ptr-string);
      else
        printf("The character was not found\n");
      return 0;
    }


    @函數名稱:   strrchr
    函數原型:   char *strrchr(const char *s, int c)
    函數功能:   得到字符串s中最后一個含有c字符的位置指針
    函數返回:   位置指針
    參數說明:
    所屬文件:   <string.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char string[15];
      char *ptr,c='r';
      strcpy(string,"This is a string");
      ptr=strrchr(string,c);
      if (ptr)
        printf("The character %c is at position:%d",c,ptr-string);
      else
        printf("The character was not found");
      return 0;
    }


    @函數名稱:   strstr
    函數原型:   char* strstr(char* str1,char* str2);
    函數功能:   找出str2字符串在str1字符串中第一次出現的位置(不包括str2的串結束符)
    函數返回:   返回該位置的指針,如找不到,返回空指針
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char *str1="Open Watcom C/C++",*str2="Watcom",*ptr;
      ptr=strstr(str1,str2);
      printf("The substring is:%s\n",ptr);
      return 0;
    }


    @函數名稱:   strrev
    函數原型:   char *strrev(char *s)
    函數功能:   將字符串中的所有字符顛倒次序排列
    函數返回:   指向s的指針
    參數說明:
    所屬文件:   <string.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char *forward="string";
      printf("Before strrev():%s",forward);
      strrev(forward);
      printf("After strrev(): %s",forward);
      return 0;
    }


    @函數名稱:   strnset
    函數原型:   char *strnset(char *s, int ch, size_t n)
    函數功能:   將字符串s中前n個字符設置為ch的值
    函數返回:   指向s的指針
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char *string="abcdefghijklmnopqrstuvwxyz";
      char letter='x';
      printf("string before strnset: %s",string);
      strnset(string,letter,13);
      printf("string after strnset: %s",string);
      return 0;
    }


    @函數名稱:   strset
    函數原型:   char *strset(char *s, int ch)
    函數功能:   將字符串s中所有字符設置為ch的值
    函數返回:   指向s的指針
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char string[10]="123456789";
      char symbol='c';
      printf("Before strset(): %s", string);
      strset(string, symbol);
      printf("After strset(): %s", string);
      return 0;
    }


    @函數名稱:   strtok
    函數原型:   char *strtok(char *s1, const char *s2)
    函數功能:   分解s1字符串為用特定分隔符分隔的多個字符串(一般用于將英文句分解為單詞)
    函數返回:   字符串s1中首次出現s2中的字符前的子字符串指針
    參數說明:   s2一般設置為s1中的分隔字符
            規定進行子調用時(即分割s1的第二、三及后續子串)第一參數必須是NULL
            在每一次匹配成功后,將s1中分割出的子串位置替換為NULL(摘下鏈中第一個環),因此s1被破壞了
            函數會記憶指針位置以供下一次調用
           
    所屬文件:   <string.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char *p;
      char *buffer;
      char *delims={ " .," };

      buffer=strdup("Find words, all of them.");
      printf("%s\n",buffer);
      p=strtok(buffer,delims);
      while(p!=NULL){
        printf("word: %s\n",p);
        p=strtok(NULL,delims);
      }
      printf("%s\n",buffer);
      return 0;
    }


    @函數名稱:   strupr
    函數原型:   char *strupr(char *s)
    函數功能:   將字符串s中的字符變為大寫
    函數返回:
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char *string="abcdefghijklmnopqrstuvwxyz",*ptr;
      ptr=strupr(string);
      printf("%s",ptr);
      return 0;
    }


    @函數名稱:   strlwr
    函數原型:   char *strlwr(char *s)
    函數功能:   將字符串中的字符變為小寫字符
    函數返回:   指向s的指針
    參數說明:
    所屬文件:   <string.h>

    #include<string.h>
    int main()
    {
      char str[]="HOW TO SAY?";
      printf("%s",strlwr(str));
      return 0;
    }


    @函數名稱:   strlen
    函數原型:   unsigned int strlen(char * str);
    函數功能:   統計字符串str中字符的個數(不包括終止符'\0')
    函數返回:   返回字符串的長度.
    參數說明:
    所屬文件:   <string.h>

    #include <stdio.h>
    #include<string.h>
    int main()
    {
      char str[]="how are you!";
      printf("the lence is:%d\n",strlen(str));
      return 0;
    }


    @函數名稱:   strerror
    函數原型:   char *strerror(int errnum)
    函數功能:   得到錯誤信息的內容信息
    函數返回:   錯誤提示信息字符串指針
    參數說明:   errnum-錯誤編號
    所屬文件:   <string.h>

    #include <stdio.h>
    #include <errno.h>
    int main()
    {
      char *buffer;
      buffer=strerror(errno);
      printf("Error: %s",buffer);
      return 0;
    }


    @函數名稱:   memcpy
    函數原型:   void *memcpy(void *dest, const void *src, size_t n)
    函數功能:   字符串拷貝
    函數返回:   指向dest的指針
    參數說明:   src-源字符串,n-拷貝的最大長度
    所屬文件:   <string.h>,<mem.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char src[]="******************************";
      char dest[]="abcdefghijlkmnopqrstuvwxyz0123456709";
      char *ptr;
      printf("destination before memcpy:%s\n",dest);
      ptr=memcpy(dest,src,strlen(src));
      if (ptr)
        printf("destination after memcpy:%s\n",dest);
      else
        printf("memcpy failed");
      return 0;
    }


    @函數名稱:   memccpy
    函數原型:   void *memccpy(void *dest, const void *src, int c, size_t n)
    函數功能:   字符串拷貝,到指定長度或遇到指定字符時停止拷貝
    函數返回:
    參數說明:   src-源字符串指針,c-中止拷貝檢查字符,n-長度,dest-拷貝底目的字符串指針
    所屬文件:   <string.h>,<mem.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char *src="This is the source string";
      char dest[50];
      char *ptr;
      ptr=memccpy(dest,src,'c',strlen(src));
      if (ptr)
      {
        *ptr='\0';
        printf("The character was found:%s",dest);
      }
      else
        printf("The character wasn't found");
      return 0;
    }


    @函數名稱:   memchr
    函數原型:   void *memchr(const void *s, int c, size_t n)
    函數功能:   在字符串中第開始n個字符中尋找某個字符c的位置
    函數返回:   返回c的位置指針,返回NULL時表示未找到
    參數說明:   s-要搜索的字符串,c-要尋找的字符,n-指定長度
    所屬文件:   <string.h>,<mem.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char str[17];
      char *ptr;
      strcpy(str,"This is a string");
      ptr=memchr(str,'r',strlen(str));
      if (ptr)
      printf("The character 'r' is at position: %d",ptr-str);
      else
      printf("The character was not found");
      return 0;
    }


    @函數名稱:   memcmp
    函數原型:   int memcmp(const void *s1, const void *s2,size_t n)
    函數功能:   按字典順序比較兩個串s1和s2的前n個字節
    函數返回:   <0,=0,>0分別表示s1<,=,>s2
    參數說明:   s1,s2-要比較的字符串,n-比較的長度
    所屬文件:   <string.h>,<mem.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char *buf1="ABCDE123";
      char *buf2="abcde456";
      int stat;
      stat=memcmp(buf1,buf2,5);
      printf("The strings to position 5 are ");
      if(stat) printf("not ");
      printf("the same\n");
      return 0;
    }



    @函數名稱:   memicmp
    函數原型:   int memicmp(const void *s1, const void *s2, size_t n)
    函數功能:   按字典順序、不考慮字母大小寫對字符串s1,s2前n個字符比較
    函數返回:   <0,=0,>0分別表示s1<,=,>s2
    參數說明:   s1,s2-要比較的字符串,n-比較的長度
    所屬文件:   <string.h>,<mem.h>

    #include <stdio.h>
    #include <string.h>
    int main()
    {
      char *buf1="ABCDE123";
      char *buf2="abcde456";
      int stat;
      stat=memicmp(buf1,buf2,5);
      printf("The strings to position 5 are ");
      if(stat) printf("not");
      printf("the same");
      return 0;
    }


    @函數名稱:   memmove
    函數原型:   void *memmove(void *dest, const void *src, size_t n)
    函數功能:   字符串拷貝
    函數返回:   指向dest的指針
    參數說明:   src-源字符串,n-拷貝的最大長度
    所屬文件:   <string.h>,<mem.h>

    #include <string.h>
    #include <stdio.h>
    int main()
    {
      char dest[40]="abcdefghijklmnopqrstuvwxyz0123456789";
      printf("destination prior to memmove:%s\n",dest);
      memmove(dest+1,dest,35);
      printf("destination after memmove:%s",dest);
      return 0;
    }


    @函數名稱:   memset
    函數原型:   void *memset(void *s, int c, size_t n)
    函數功能:   字符串中的n個字節內容設置為c
    函數返回:
    參數說明:   s-要設置的字符串,c-設置的內容,n-長度
    所屬文件:   <string.h>,<mem.h>

    #include <string.h>
    #include <stdio.h>
    #include <mem.h>
    int main()
    {
      char buffer[]="Hello world";
      printf("Buffer before memset:%s\n",buffer);
      memset(buffer,'*',strlen(buffer)-1);
      printf("Buffer after memset:%s",buffer);
      return 0;
    }

    posted @ 2006-03-09 16:54 小鐵匠 閱讀(2009) | 評論 (0)編輯 收藏

    C++字符串完全指南 - Win32字符編碼(一)

    前言

    字符串的表現形式各異,象TCHAR,std::string,BSTR等等,有時還會見到怪怪的用_tcs起頭的宏。這個指南的目的就是說明各種字符串類型及其用途,并說明如何在必要時進行類型的相互轉換。

    在指南的第一部分,介紹三種字符編碼格式。理解編碼的工作原理是致為重要的。即使你已經知道字符串是一個字符的數組這樣的概念,也請閱讀本文,它會讓你明白各種字符串類之間的關系。

    指南的第二部分,將闡述各個字符串類,什么時候使用哪種字符串類,及其相互轉換。

    字符串基礎 - ASCII, DBCS, Unicode

    所有的字符串類都起源于C語言的字符串,而C語言字符串則是字符的數組。首先了解一下字符類型。有三種編碼方式和三種字符類型。

    第一種編碼方式是單字節字符集,稱之為SBCS,它的所有字符都只有一個字節的長度。ASCII碼就是SBCS。SBCS字符串由一個零字節結尾。

    第二種編碼方式是多字節字符集,稱之為MBCS,它包含的字符中有單字節長的字符,也有多字節長的字符。Windows用到的MBCS只有二種字符類型,單字節字符和雙字節字符。因此Windows中用得最多的字符是雙字節字符集,即DBCS,通常用它來代替MBCS。

    在DBCS編碼中,用一些保留值來指明該字符屬于雙字節字符。例如,Shift-JIS(通用日語)編碼中,值0x81-0x9F 和 0xE0-0xFC 的意思是:“這是一個雙字節字符,下一個字節是這個字符的一部分”。這樣的值通常稱為前導字節(lead byte),總是大于0x7F。前導字節后面是跟隨字節(trail byte)。DBCS的跟隨字節可以是任何非零值。與SBCS一樣,DBCS字符串也由一個零字節結尾。

    第三種編碼方式是Unicode。Unicode編碼標準中的所有字符都是雙字節長。有時也將Unicode稱為寬字符集(wide characters),因為它的字符比單字節字符更寬(使用更多內存)。注意,Unicode不是MBCS - 區別在于MBCS編碼中的字符長度是不同的。Unicode字符串用二個零字節字符結尾(一個寬字符的零值編碼)。

    單字節字符集是拉丁字母,重音文字,用ASCII標準定義,用于DOS操作系統。雙字節字符集用于東亞和中東語言。Unicode用于COM和Windows NT內部。

    讀者都很熟悉單字節字符集,它的數據類型是char。雙字節字符集也使用char數據類型(雙字節字符集中的許多古怪處之一)。Unicode字符集用wchar_t數據類型。Unicode字符串用L前綴起頭,如:

      wchar_t  wch = L'1';      // 2 個字節, 0x0031

      wchar_t* wsz = L"Hello";  // 12 個字節, 6 個寬字符

    字符串的存儲

    單字節字符串順序存放各個字符,并用零字節表示字符串結尾。例如,字符串"Bob"的存儲格式為:

    Unicode編碼中,L"Bob"的存儲格式為:

    用0x0000 (Unicode的零編碼)結束字符串。

    DBCS 看上去有點象SBCS。以后我們會看到在串處理和指針使用上是有微妙差別的。字符串"日本語" (nihongo) 的存儲格式如下(用LB和TB分別表示前導字節和跟隨字節):

    注意,"ni"的值不是WORD值0xFA93。值93和FA順序組合編碼為字符"ni"。(在高位優先CPU中,存放順序正如上所述)。

    字符串處理函數

    C語言字符串處理函數,如strcpy(), sprintf(), atol()等只能用于單字節字符串。在標準庫中有只用于Unicode字符串的函數,如wcscpy(), swprintf(), _wtol()。

    微軟在C運行庫(CRT)中加入了對DBCS字符串的支持。對應于strxxx()函數,DBCS使用_mbsxxx()函數。在處理DBCS字符串(如日語,中文,或其它DBCS)時,就要用_mbsxxx()函數。這些函數也能用于處理SBCS字符串(因為DBCS字符串可能就只含有單字節字符)。

    現在用一個示例來說明字符串處理函數的不同。如有Unicode字符串L"Bob":

    x86 CPU的排列順序是低位優先(little-endian)的,值0x0042的存儲順序為42 00。這時如用strlen()函數求字符串的長度就發生問題。函數找到第一個字節42,然后是00,意味著字符串結尾,于是返回1。反之,用wcslen()函數求"Bob"的長度更糟糕。wcslen()首先找到0x6F42,然后是0x0062,以后就在內存緩沖內不斷地尋找00 00直至發生一般性保護錯(GPF)。

    strxxx()及其對應的_mbsxxx()究竟是如何運作的?二者之間的不同是非常重要的,直接影響到正確遍歷DBCS字符串的方法。下面先介紹字符串遍歷,然后再回來討論strxxx()和 _mbsxxx()。

    字符串遍歷

    我們中的大多數人都是從SBCS成長過來的,都習慣于用指針的 ++ 和 -- 操作符來遍歷字符串,有時也使用數組來處理字符串中的字符。這二種方法對于SBCS 和 Unicode 字符串的操作都是正確無誤的,因為二者的字符都是等長的,編譯器能夠的正確返回我們尋求的字符位置。

    但對于DBCS字符串就不能這樣了。用指針訪問DBCS字符串有二個原則,打破這二個原則就會造成錯誤。

    1. 不可使用 ++ 算子,除非每次都檢查是否為前導字節。

    2. 絕不可使用 -- 算子來向后遍歷。

    先說明原則2,因為很容易找到一個非人為的示例。假設,有一個配制文件,程序啟動時要從安裝路徑讀取該文件,如:C:\Program Files\MyCoolApp\config.bin。文件本身是正常的。

    假設用以下代碼來配制文件名:

    bool GetConfigFileName ( char* pszName, size_t nBuffSize )
    {
    char szConfigFilename[MAX_PATH];
        // 這里從注冊表讀取文件的安裝路徑,假設一切正常。
        // 如果路徑末尾沒有反斜線,就加上反斜線。
        // 首先,用指針指向結尾零:
    char* pLastChar = strchr ( szConfigFilename, '\0' );
        // 然后向后退一個字符:
        pLastChar--;  
        if ( *pLastChar != '\\' )
            strcat ( szConfigFilename, "\\" );
        // 加上文件名:
        strcat ( szConfigFilename, "config.bin" );
        // 如果字符串長度足夠,返回文件名:
        if ( strlen ( szConfigFilename ) >= nBuffSize )
            return false;
        else
            {
            strcpy ( pszName, szConfigFilename );
            return true;
            }
    }

    這段代碼的保護性是很強的,但用到DBCS字符串還是會出錯。假如文件的安裝路徑用日語表達:C:\ヨウユソ,該字符串的內存表達為:

    這時用上面的GetConfigFileName()函數來檢查文件路徑末尾是否含有反斜線就會出錯,得到錯誤的文件名。

    錯在哪里?注意上面的二個十六進制值0x5C(藍色)。前面的0x5C是字符"\",后面則是字符值83 5C,代表字符"ソ"。可是函數把它誤認為反斜線了。

    正確的方法是用DBCS函數將指針指向恰當的字符位置,如下所示:

    bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
    {
    char szConfigFilename[MAX_PATH];
        // 這里從注冊表讀取文件的安裝路徑,假設一切正常。
        // 如果路徑末尾沒有反斜線,就加上反斜線。
        // 首先,用指針指向結尾零:
    char* pLastChar = _mbschr ( szConfigFilename, '\0' );
        // 然后向后退一個雙字節字符:
        pLastChar = CharPrev ( szConfigFilename, pLastChar );
        if ( *pLastChar != '\\' )
            _mbscat ( szConfigFilename, "\\" );
        // 加上文件名:
        _mbscat ( szConfigFilename, "config.bin" );
        // 如果字符串長度足夠,返回文件名:
        if ( _mbslen ( szInstallDir ) >= nBuffSize )
            return false;
        else
            {
            _mbscpy ( pszName, szConfigFilename );
            return true;
            }
    } 

    這個改進的函數用CharPrev() API 函數將指針pLastChar向后移動一個字符。如果字符串末尾的字符是雙字節字符,就向后移動2個字節。這時返回的結果是正確的,因為不會將字符誤判為反斜線。

    現在可以想像到第一原則了。例如,要遍歷字符串尋找字符":",如果不使用CharNext()函數而使用++算子,當跟隨字節值恰好也是":"時就會出錯。

    與原則2相關的是數組下標的使用:

     2a. 絕不可在字符串數組中使用遞減下標。

    出錯原因與原則2相同。例如,設置指針pLastChar為:

    char* pLastChar = &szConfigFilename [strlen(szConfigFilename) - 1];

    結果與原則2的出錯一樣。下標減1就是指針向后移動一個字節,不符原則2。

    再談strxxx() _mbsxxx()

    現在可以清楚為什么要用 _mbsxxx() 函數了。strxxx() 函數不認識DBCS字符而 _mbsxxx()認識。如果調用strrchr("C:\\", '\\')函數可能會出錯,但 _mbsrchr()認識雙字節字符,所以能返回指向最后出現反斜線字符的指針位置。

    最后提一下strxxx() 和 _mbsxxx() 函數族中的字符串長度測量函數,它們都返回字符串的字節數。如果字符串含有3個雙字節字符,_mbslen()將返回6。而Unicode的函數返回的是wchar_ts的數量,如wcslen(L"Bob") 返回3

    C++字符串完全指南 - Win32字符編碼(二)
    翻譯:連波
    15/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098306,00.htm

    Win32 API中的MBCS Unicode

    API的二個字符集

    也許你沒有注意到,Win32的API和消息中的字符串處理函數有二種,一種為MCBS字符串,另一種為Unicode字符串。例如,Win32中沒有SetWindowText()這樣的接口,而是用SetWindowTextA()和 SetWindowTextW()函數。后綴A (表示ANSI)指明是MBCS函數,后綴W(表示寬字符)指明是Unicode函數。

    編寫Windows程序時,可以選擇用MBCS或Unicode API接口函數。用VC AppWizards向導時,如果不修改預處理器設置,缺省使用的是MBCS函數。但是在API接口中沒有SetWindowText()函數,該如何調用呢?實際上,在winuser.h頭文件中做了以下定義:

    BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
    BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );
    #ifdef UNICODE
     #define SetWindowText  SetWindowTextW
    #else
     #define SetWindowText  SetWindowTextA
    #endif

    編寫MBCS應用時,不必定義UNICODE,預處理為:

    #define SetWindowText  SetWindowTextA

    然后將SetWindowText()處理為真正的API接口函數SetWindowTextA() (如果愿意的話,可以直接調用SetWindowTextA() 或SetWindowTextW()函數,不過很少有此需要)。

    如果要將缺省應用接口改為Unicode,就到預處理設置的預處理標記中去掉 _MBCS標記,加入UNICODE 和 _UNICODE (二個標記都要加入,不同的頭文件使用不同的標記)。不過,這時要處理普通字符串反而會遇到問題。如有代碼:

    HWND hwnd = GetSomeWindowHandle();
    char szNewText[] = "we love Bob!";
    SetWindowText ( hwnd, szNewText );

    編譯器將"SetWindowText"置換為"SetWindowTextW"后,代碼變為:

    HWND hwnd = GetSomeWindowHandle();
    char szNewText[] = "we love Bob!";
    SetWindowTextW ( hwnd, szNewText );

    看出問題了吧,這里用一個Unicode字符串處理函數來處理單字節字符串。

    第一種解決辦法是使用宏定義:
    HWND hwnd = GetSomeWindowHandle();
    #ifdef UNICODE
     wchar_t szNewText[] = L"we love Bob!";
    #else
     char szNewText[] = "we love Bob!";
    #endif
    SetWindowText ( hwnd, szNewText );

    要對每一個字符串都做這樣的宏定義顯然是令人頭痛的。所以用TCHAR來解決這個問題:

    TCHAR的救火角色

    TCHAR 是一種字符類型,適用于MBCS 和 Unicode二種編碼。程序中也不必到處使用宏定義。

    TCHAR的宏定義如下:

    #ifdef UNICODE
     typedef wchar_t TCHAR;
    #else
     typedef char TCHAR;
    #endif

    所以,TCHAR中在MBCS程序中是char類型,在Unicode中是 wchar_t 類型。

    對于Unicode字符串,還有個 _T() 宏,用于解決 L 前綴:

    #ifdef UNICODE
     #define _T(x) L##x
    #else
     #define _T(x) x
    #endif

    ## 是預處理算子,將二個變量粘貼在一起。不管什么時候都對字符串用 _T 宏處理,這樣就可以在Unicode編碼中給字符串加上L前綴,如:

    TCHAR szNewText[] = _T("we love Bob!");

    SetWindowTextA/W 函數族中還有其它隱藏的宏可以用來代替strxxx() 和 _mbsxxx() 字符串函數。例如,可以用 _tcsrchr 宏取代strrchr(),_mbsrchr(),或 wcsrchr()函數。_tcsrchr 根據編碼標記為_MBCS 或 UNICODE,將右式函數做相應的擴展處理。宏定義方法類似于SetWindowText。

    不止strxxx()函數族中有TCHAR宏定義,其它一些函數中也有。例如,_stprintf (取代sprintf()和swprintf()),和 _tfopen (取代fopen() 和 _wfopen())。MSDN的全部宏定義在"Generic-Text Routine Mappings"欄目下。

    String 和 TCHAR 類型定義

    Win32 API 文件中列出的函數名都是通用名(如"SetWindowText"),所有的字符串都按照TCHAR類型處理。(只有XP除外,XP只使用Unicode類型)。下面是MSDN給出的常用類型定義:

     

    類型

    MBCS 編碼中的意義

    Unicode 編碼中的意義

    WCHAR

    wchar_t

    wchar_t

    LPSTR

    zero-terminated string of char (char*)

    zero-terminated string of char (char*)

    LPCSTR

    constant zero-terminated string of char (constchar*)

    constant zero-terminated string of char (constchar*)

    LPWSTR

    zero-terminated Unicode string (wchar_t*)

    zero-terminated Unicode string (wchar_t*)

    LPCWSTR

    constant zero-terminated Unicode string (const wchar_t*)

    constant zero-terminated Unicode string (const wchar_t*)

    TCHAR

    char

    wchar_t

    LPTSTR

    zero-terminated string of TCHAR (TCHAR*)

    zero-terminated string of TCHAR (TCHAR*)

    LPCTSTR

    constant zero-terminated string of TCHAR (const TCHAR*)

    constant zero-terminated string of TCHAR (const TCHAR*)

    何時使用TCHAR 和Unicode

    可能會有疑問:“為什么要用Unicode?我一直用的都是普通字符串。”

    在三種情況下要用到Unicode:

    1. 程序只運行于Windows NT。
    2. 處理的字符串長于MAX_PATH定義的字符數。
    3. 程序用于Windows XP中的新接口,那里沒有A/W版本之分。

    大部分Unicode API不可用于Windows 9x。所以如果程序要在Windows 9x上運行的話,要強制使用MBCS API (微軟推出一個可運行于Windows 9x的新庫,叫做Microsoft Layer for Unicode。但我沒有試用過,無法說明它的好壞)。相反,NT內部全部使用Unicode編碼,使用Unicode API可以加速程序運行。每當將字符串處理為MBCS API時,操作系統都會將字符串轉換為Unicode并調用相應的Unicode API 函數。對于返回的字符串,操作系統要做同樣的轉換。盡管這些轉換經過了高度優化,模塊盡可能地壓縮到最小,但畢竟會影響到程序的運行速度。

    NT允許使用超長文件名(長于MAX_PATH 定義的260),但只限于Unicode API使用。Unicode API的另外一個優點是程序能夠自動處理輸入的文字語言。用戶可以混合輸入英文,中文和日文作為文件名。不必使用其它代碼來處理,都按照Unicode編碼方式處理。

    最后,作為Windows 9x的結局,微軟似乎拋棄了MBCS API。例如,SetWindowTheme() 接口函數的二個參數只支持Unicode編碼。使用Unicode編碼省卻了MBCS與Unicode之間的轉換過程。

    如果程序中還沒有使用到Unicode編碼,要堅持使用TCHAR和相應的宏。這樣不但可以長期保持程序中DBCS編碼的安全性,也利于將來擴展使用到Unicode編碼。那時只要改變預處理中的設置即可!

    C++字符串完全指南(2) - 各種字符串類(一)
    翻譯:連波
    19/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098621,00.htm

    前言

    C語言的字符串容易出錯,難以管理,并且往往是黑客到處尋找的目標。于是,出現了許多字符串包裝類。可惜,人們并不很清楚什么情況下該用哪個類,也不清楚如何將C語言字符串轉換到包裝類。

    本文涉及到Win32 API,MFC,STL,WTL和Visual C++運行庫中使用到的所有的字符串類型。說明各個類的用法,如何構造對象,如何進行類轉換等等。Nish為本文提供了Visual C++ 7的managed string 類的用法。

    閱讀本文之前,應完全理解本指南第一部分中闡述的字符類型和編碼。

    字符串類的首要原則:

    不要隨便使用類型強制轉換,除非轉換的類型是明確由文檔規定的。

    之所以撰寫字符串指南這二篇文章,是因為常有人問到如何將X類型的字符串轉換到Z類型。提問者使用了強制類型轉換(cast),但不知道為什么不能轉換成功。各種各樣的字符串類型,特別是BSTR,在任何場合都不是三言二語可以講清的。因此,我以為這些提問者是想讓強制類型轉換來處理一切。

    除非明確規定了轉換算子,不要將任何其它類型數據強制轉換為string。一個字符串不能用強制類型轉換到string類。例如:

    void SomeFunc ( LPCWSTR widestr );
    main()
    {
      SomeFunc ( (LPCWSTR) "C:\\foo.txt" );  // 錯!
    }
    
    

    這段代碼100%錯誤。它可以通過編譯,因為類型強制轉換超越了編譯器的類型檢驗。但是,能夠通過編譯,并不證明代碼是正確的。

    下面,我將指出什么時候用類型強制轉換是合理的。
    C語言字符串與類型定義

    如指南的第一部分所述,Windows API定義了TCHAR術語。它可用于MBCS或Unicode編碼字符,取決于預處理設置為_MBCS 或 _UNICODE標記。關于TCHAR的詳細說明請閱指南的第一部分。為便于敘述,下面給出字符類型定義:

    Type

    Meaning

    WCHAR

    Unicode character (wchar_t)

    TCHAR

    MBCS or Unicode character, depending on preprocessor settings

    LPSTR

    string of char (char*)

    LPCSTR

    constant string of char (constchar*)

    LPWSTR

    string of WCHAR (WCHAR*)

    LPCWSTR

    constant string of WCHAR (const WCHAR*)

    LPTSTR

    string of TCHAR (TCHAR*)

    LPCTSTR

    constant string of TCHAR (const TCHAR*)

    另外還有一個字符類型OLECHAR。這是一種對象鏈接與嵌入的數據類型(比如嵌入Word文檔)。這個類型通常定義為wchar_t。如果將預處理設置定義為OLE2ANSI,OLECHAR將被定義為char類型。現在已經不再定義OLE2ANSI(它只在MFC 3以前版本中使用),所以我將OLECHAR作為Unicode字符處理。

    下面是與OLECHAR相關的類型定義:

    Type

    Meaning

    OLECHAR

    Unicode character (wchar_t)

    LPOLESTR

    string of OLECHAR (OLECHAR*)

    LPCOLESTR

    constant string of OLECHAR (const OLECHAR*)

    還有以下二個宏讓相同的代碼能夠適用于MBCS和Unicode編碼:

    Type

    Meaning

    _T(x)

    Prepends L to the literal in Unicode builds.

    OLESTR(x)

    Prepends L to the literal to make it an LPCOLESTR.

    宏_T有幾種形式,功能都相同。如: -- TEXT, _TEXT, __TEXT, 和 __T這四種宏的功能相同。

    COM中的字符串 - BSTR VARIANT

    許多COM接口使用BSTR聲明字符串。BSTR有一些缺陷,所以我在這里讓它獨立成章。

    BSTR是Pascal類型字符串(字符串長度值顯式地與數據存放在一起)和C類型字符串(字符串長度必須通過尋找到結尾零字符來計算)的混合型字符串。BSTR屬于Unicode字符串,字符串中預置了字符串長度值,并且用一個零字符來結尾。下面是一個"Bob"的BSTR字符串:

    注意,字符串長度值是一個DWORD類型值,給出字符串的字節長度,但不包括結尾零。在上例,"Bob"含有3個Unicode字符(不計結尾零),6個字節長。因為明確給出了字符串長度,所以當BSTR數據在不同的處理器和計算機之間傳送時,COM庫能夠知道應該傳送的數據量。

    附帶說一下,BSTR可以包含任何數據塊,不單是字符。它甚至可以包容內嵌零字符數據。這些不在本文討論范圍。

    C++中的BSTR變量其實就是指向字符串首字符的指針。BSTR是這樣定義的:

    typedef OLECHAR* BSTR;

    這個定義很糟糕,因為事實上BSTR與Unicode字符串不一樣。有了這個類型定義,就越過了類型檢查,可以混合使用LPOLESTR和BSTR。向一個需要LPCOLESTR (或 LPCWSTR)類型數據的函數傳遞BSTR數據是安全的,反之則不然。所以要清楚了解函數所需的字符串類型,并向函數傳遞正確類型的字符串。

    要知道為什么向一個需要BSTR類型數據的函數傳遞LPCWSTR類型數據是不安全的,就別忘了BSTR必須在字符串開頭的四個字節保留字符串長度值。但LPCWSTR字符串中沒有這個值。當其它的處理過程(如Word)要尋找BSTR的長度值時就會找到一堆垃圾或堆棧中的其它數據或其它隨機數據。這就導致方法失效,當長度值太大時將導致崩潰。

    許多應用接口都使用BSTR,但都用到二個最重要的函數來構造和析構BSTR。就是SysAllocString()和SysFreeString()函數。SysAllocString()將Unicode字符串拷貝到BSTR,SysFreeString()釋放BSTR。示例如下:

    BSTR bstr = NULL;
    bstr = SysAllocString ( L"Hi Bob!" );
    if ( NULL == bstr )
        // 內存溢出
       // 這里使用bstr
    SysFreeString ( bstr );
    
    

    當然,各種BSTR包裝類都會小心地管理內存。

    自動接口中的另一個數據類型是VARIANT。它用于在無類型語言,諸如JScript,VBScript,以及Visual Basic,之間傳遞數據。VARIANT可以包容許多不用類型的數據,如long和IDispatch*。如果VARIANT包含一個字符串,這個字符串是BSTR類型。在下文的VARIANT包裝類中我還會談及更多的VARIANT。
    C++字符串完全指南(2) - 各種字符串類- CRT類
    翻譯:連波
    20/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098682,00.htm

    _bstr_t

    字符串包裝類

    我已經說明了字符串的各種類型,現在討論包裝類。對于每個包裝類,我都會說明它的對象構造過程和如何轉換成C類型字符串指針。應用接口的調用,或構造另一個不同類型的字符串類,大多都要用到C類型指針。本文不涉及類的其它操作,如排序和比較等。

    再強調一下,在完全了解轉換結果之前不要隨意使用強制類型轉換。

    CRT類

    _bstr_t

    _bstr_t 是BSTR的完全包裝類。實際上,它隱含了BSTR。它提供多種構造函數,能夠處理隱含的C類型字符串。但它本身卻不提供BSTR的處理機制,所以不能作為COM方法的輸出參數[out]。如果要用到BSTR* 類型數據,用ATL的CComBSTR類更為方便。

    _bstr_t 數據可以傳遞給需要BSTR數據的函數,但必須滿足以下三個條件:

    首先,_bstr_t 具有能夠轉換為wchar_t*類型數據的函數。

    其次,根據BSTR定義,使得wchar_t* 和BSTR對于編譯器來說是相同的。

    第三,_bstr_t內部保留的指向內存數據塊的指針 wchar_t* 要遵循BSTR格式。

    滿足這些條件,即使沒有相應的BSTR轉換文檔,_bstr_t 也能正常工作。示例如下:

     // 構造
    _bstr_t bs1 = "char string";        // 從LPCSTR構造 
    _bstr_t bs2 = L"wide char string"; // 從LPCWSTR構造
    _bstr_t bs3 = bs1;              // 拷貝另一個 _bstr_t
    _variant_t v = "Bob";
    _bstr_t bs4 = v;              // 從一個含有字符串的 _variant_t 構造
    // 數據萃取
    LPCSTR psz1 = bs1;              // 自動轉換到MBCS字符串
    LPCSTR psz2 = (LPCSTR) bs1;     // cast OK, 同上
    LPCWSTR pwsz1 = bs1;            // 返回內部的Unicode字符串
    LPCWSTR pwsz2 = (LPCWSTR) bs1;  // cast OK, 同上
    BSTR    bstr = bs1.copy();      // 拷貝bs1, 返回BSTR
    // ...
      SysFreeString ( bstr );
    

    注意,_bstr_t 也可以轉換為char* 和 wchar_t*。這是個設計問題。雖然char* 和 wchar_t*不是常量指針,但不能用于修改字符串,因為可能會打破內部BSTR結構。

    _variant_t
    _variant_t

    _variant_t 是VARIANT的完全包裝類。它提供多種構造函數和數據轉換函數。本文僅討論與字符串有關的操作。

    // 構造
    _variant_t v1 = "char string"; // 從LPCSTR 構造
    _variant_t v2 = L"wide char string"; // 從LPCWSTR 構造
    _bstr_t bs1 = "Bob";
    _variant_t v3 = bs1; // 拷貝一個 _bstr_t 對象
    // 數據萃取
    _bstr_t bs2 = v1; // 從VARIANT中提取BSTR
    _bstr_t bs3 = (_bstr_t) v1; // cast OK, 同上
    

    注意,_variant_t 方法在轉換失敗時會拋出異常,所以要準備用catch 捕捉_com_error異常。

    另外要注意 _variant_t 不能直接轉換成MBCS字符串。要建立一個過渡的_bstr_t 變量,用其它提供轉換Unicode到MBCS的類函數,或ATL轉換宏來轉換。

    與_bstr_t 不同,_variant_t 數據可以作為參數直接傳送給COM方法。_variant_t 繼承了VARIANT類型,所以在需要使用VARIANT的地方使用_variant_t 是C++語言規則允許的。
    C++字符串完全指南(2) - STL和ATL類
    翻譯:連波
    21/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098845,00.htm

    STL類

    STL類

    STL只有一個字符串類,即basic_string。basic_string管理一個零結尾的字符數組。字符類型由模板參數決定。通常,basic_string被處理為不透明對象。可以獲得一個只讀指針來訪問緩沖區,但寫操作都是由basic_string的成員函數進行的。

    basic_string預定義了二個特例:string,含有char類型字符;which,含有wchar_t類型字符。沒有內建的TCHAR特例,可用下面的代碼實現:

    // 特例化
    typedef basic_string tstring; // TCHAR字符串
    // 構造
    string str = "char string"; // 從LPCSTR構造
    wstring wstr = L"wide char string"; // 從LPCWSTR構造
    tstring tstr = _T("TCHAR string"); // 從LPCTSTR構造
    // 數據萃取
    LPCSTR psz = str.c_str(); // 指向str緩沖區的只讀指針
    LPCWSTR pwsz = wstr.c_str(); // 指向wstr緩沖區的只讀指針
    LPCTSTR ptsz = tstr.c_str(); // 指向tstr緩沖區的只讀指針
    
    

    與_bstr_t 不同,basic_string不能在字符集之間進行轉換。但是如果一個構造函數接受相應的字符類型,可以將由c_str()返回的指針傳遞給這個構造函數。例如:

    // 從basic_string構造_bstr_t 
    _bstr_t bs1 = str.c_str();  // 從LPCSTR構造 _bstr_t
    _bstr_t bs2 = wstr.c_str(); // 從LPCWSTR構造 _bstr_t
    
    ATL類
    CComBSTR

    CComBSTR 是ATL的BSTR包裝類。某些情況下比_bstr_t 更有用。最主要的是,CComBSTR允許操作隱含BSTR。就是說,傳遞一個CComBSTR對象給COM方法時,CComBSTR對象會自動管理BSTR內存。例如,要調用下面的接口函數:

    // 簡單接口
    struct IStuff : public IUnknown
    {
      // 略去COM程序...
      STDMETHOD(SetText)(BSTR bsText);
      STDMETHOD(GetText)(BSTR* pbsText);
    };
    

    CComBSTR 有一個BSTR操作方法,能將BSTR直接傳遞給SetText()。還有一個引用操作(operator &)方法,返回BSTR*,將BSTR*傳遞給需要它的有關函數。

    CComBSTR bs1;
    CComBSTR bs2 = "new text";
    pStuff->GetText ( &bs1 );       // ok, 取得內部BSTR地址
      pStuff->SetText ( bs2 );        // ok, 調用BSTR轉換
      pStuff->SetText ( (BSTR) bs2 ); // cast ok, 同上
    

    CComVariant
    CComBSTR有類似于 _bstr_t 的構造函數。但沒有內建MBCS字符串的轉換函數。可以調用ATL宏進行轉換。

    // 構造
    CComBSTR bs1 = "char string"; // 從LPCSTR構造
    CComBSTR bs2 = L"wide char string"; // 從LPCWSTR構造
    CComBSTR bs3 = bs1; // 拷貝CComBSTR
    CComBSTR bs4;
    bs4.LoadString ( IDS_SOME_STR ); // 從字符串表加載
    // 數據萃取
    BSTR bstr1 = bs1; // 返回內部BSTR,但不可修改!
    BSTR bstr2 = (BSTR) bs1; // cast ok, 同上
    BSTR bstr3 = bs1.Copy(); // 拷貝bs1, 返回BSTR
    BSTR bstr4;
    bstr4 = bs1.Detach(); // bs1不再管理它的BSTR
    // ...
    SysFreeString ( bstr3 );
    SysFreeString ( bstr4 );
    
    

    上面的最后一個示例用到了Detach()方法。該方法調用后,CComBSTR對象就不再管理它的BSTR或其相應內存。所以bstr4就必須調用SysFreeString()。

    最后討論一下引用操作符(operator &)。它的超越使得有些STL集合(如list)不能直接使用CComBSTR。在集合上使用引用操作返回指向包容類的指針。但是在CComBSTR上使用引用操作,返回的是BSTR*,不是CComBSTR*。不過可以用ATL的CAdapt類來解決這個問題。例如,要建立一個CComBSTR的隊列,可以聲明為:

      std::list< CAdapt> bstr_list;

    CAdapt 提供集合所需的操作,是隱含于代碼的。這時使用bstr_list 就象在操作一個CComBSTR隊列。

    CComVariant

    CComVariant 是VARIANT的包裝類。但與 _variant_t 不同,它的VARIANT不是隱含的,可以直接操作類里的VARIANT成員。CComVariant 提供多種構造函數和多類型操作。這里只介紹與字符串有關的操作。

    // 構造
    CComVariant v1 = "char string";       // 從LPCSTR構造
    CComVariant v2 = L"wide char string"; // 從LPCWSTR構造
    CComBSTR bs1 = "BSTR bob";
    CComVariant v3 = (BSTR) bs1;          // 從BSTR拷貝
    // 數據萃取
    CComBSTR bs2 = v1.bstrVal;            // 從VARIANT提取BSTR
    

    跟_variant_t 不同,CComVariant沒有不同VARIANT類型之間的轉換操作。必須直接操作VARIANT成員,并確定該VARIANT的類型無誤。調用ChangeType()方法可將CComVariant數據轉換為BSTR。

    CComVariant v4 = ... // 從某種類型初始化 v4
    CComBSTR bs3;
    if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
        bs3 = v4.bstrVal;
    

    跟 _variant_t 一樣,CComVariant不能直接轉換為MBCS字符串。要建立一個過渡的_bstr_t 變量,用其它提供轉換Unicode到MBCS的類函數,或ATL轉換宏來轉換。

    ATL轉換宏

    ATL轉換宏

    ATL的字符串轉換宏可以方便地轉換不同編碼的字符,用在函數中很有效。宏按照[source type]2[new type] 或 [source type]2C[new type]格式命名。后者轉換為一個常量指針 (名字內含"C")。類型縮寫如下:


     A:MBCS字符串,char* (A for ANSI)
     W:Unicode字符串,wchar_t* (W for wide)
     T:TCHAR字符串,TCHAR*
     OLE:OLECHAR字符串,OLECHAR* (實際等于W)
     BSTR:BSTR (只用于目的類型)

    例如,W2A() 將Unicode字符串轉換為MBCS字符串,T2CW()將TCHAR字符串轉換為Unicode字符串常量。

    要使用宏轉換,程序中要包含atlconv.h頭文件。可以在非ATL程序中使用宏轉換,因為頭文件不依賴其它的ATL,也不需要 _Module全局變量。如在函數中使用轉換宏,在函數起始處先寫上USES_CONVERSION宏。它表明某些局部變量由宏控制使用。

    轉換得到的結果字符串,只要不是BSTR,都存儲在堆棧中。如果要在函數外使用這些字符串,就要將這些字符串拷貝到其它的字符串類。如果結果是BSTR,內存不會自動釋放,因此必須將返回值分配給一個BSTR變量或BSTR的包裝類,以避免內存泄露。

    下面是若干宏轉換示例:

    // 帶有字符串的函數:
    void Foo ( LPCWSTR wstr );
    void Bar ( BSTR bstr );
    // 返回字符串的函數:
    void Baz ( BSTR* pbstr );
    #include 
    main()
    {
    using std::string;
    USES_CONVERSION;    // 聲明局部變量由宏控制使用
    // 示例1:送一個MBCS字符串到Foo()
    LPCSTR psz1 = "Bob";
    string str1 = "Bob";
    Foo ( A2CW(psz1) );
      Foo ( A2CW(str1.c_str()) );
    // 示例2:將MBCS字符串和Unicode字符串送到Bar()
    LPCSTR psz2 = "Bob";
    LPCWSTR wsz = L"Bob";
    BSTR bs1;
    CComBSTR bs2;
    bs1 = A2BSTR(psz2);         // 創建 BSTR
      bs2.Attach ( W2BSTR(wsz) ); // 同上,分配到CComBSTR
    Bar ( bs1 );
      Bar ( bs2 );
    SysFreeString ( bs1 );      // 釋放bs1
      // 不必釋放bs2,由CComBSTR釋放。
    // 示例3:轉換由Baz()返回的BSTR
    BSTR bs3 = NULL;
    string str2;
    Baz ( &bs3 );          // Baz() 填充bs3內容
    str2 = W2CA(bs3);      // 轉換為MBCS字符串
      SysFreeString ( bs3 ); // 釋放bs3
    }
    
    

    可以看到,向一個需要某種類型參數的函數傳遞另一種類型的參數,用宏轉換是非常方便的。
    C++字符串完全指南(2) - MFC類
    翻譯:連波
    22/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39098983,00.htm

    MFC類

    MFC類

    CString

    MFC的CString含有TCHAR,它的實際字符類型取決于預處理標記的設置。通常,CString象STL字符串一樣是不透明對象,只能用CString的方法來修改。CString比STL字符串更優越的是它的構造函數接受MBCS和Unicode字符串。并且可以轉換為LPCTSTR,因此可以向接受LPCTSTR的函數直接傳遞CString對象,不必調用c_str()方法。

    // 構造
    CString s1 = "char string"; // 從LPCSTR構造
    CString s2 = L"wide char string"; // 從LPCWSTR構造
    CString s3 ( ' ', 100 ); // 預分配100字節,填充空格
    CString s4 = "New window text";
    // 可以在LPCTSTR處使用CString:
    SetWindowText ( hwndSomeWindow, s4 );
    // 或者,顯式地做強制類型轉換:
    SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );
    
    

    也可以從字符串表加載字符串。CString通過LoadString()來構造對象。用Format()方法可有選擇地從字符串表讀取一定格式的字符串。

    // 從字符串表構造/加載
    CString s5 ( (LPCTSTR) IDS_SOME_STR );  // 從字符串表加載
    CString s6, s7;
    // 從字符串表加載
      s6.LoadString ( IDS_SOME_STR );
    // 從字符串表加載打印格式的字符串
      s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );
    

    第一個構造函數看上去有點怪,但它的確是文檔標定的字符串加載方式。

    注意,CString只允許一種強制類型轉換,即強制轉換為LPCTSTR。強制轉換為LPTSTR (非常量指針)是錯誤的。按照老習慣,將CString強制轉換為LPTSTR只能傷害自己。有時在程序中沒有發現出錯,那只是碰巧。轉換到非常量指針的正確方法是調用GetBuffer()方法。

    下面以往隊列加入元素為例說明如何正確地使用CString:

    CString str = _T("new text");
    LVITEM item = {0};
    item.mask = LVIF_TEXT;
      item.iItem = 1;
      item.pszText = (LPTSTR)(LPCTSTR) str; // 錯!
      item.pszText = str.GetBuffer(0);      // 正確
    ListView_SetItem ( &item );
      str.ReleaseBuffer();  // 將隊列返回給str
    

    pszText成員是LPTSTR,一個非常量指針,因此要用str的GetBuffer()。GetBuffer()的參數是CString分配的最小緩沖區。如果要分配一個1K的TCHAR,調用GetBuffer(1024)。參數為0,只返回指向字符串的指針。

    上面示例的出錯語句可以通過編譯,甚至可以正常工作,如果恰好就是這個類型。但這不證明語法正確。進行非常量的強制類型轉換,打破了面向對象的封裝原則,并逾越了CString的內部操作。如果你習慣進行這樣的強制類型轉換,終會遇到出錯,可你未必知道錯在何處,因為你到處都在做這樣的轉換,而代碼也都能運行。

    知道為什么人們總在抱怨有缺陷的軟件嗎?不正確的代碼就臭蟲的滋生地。然道你愿意編寫明知有錯的代碼讓臭蟲有機可乘?還是花些時間學習CString的正確用法讓你的代碼能夠100%的正確吧。

    CString還有二個函數能夠從CString中得到BSTR,并在必要時轉換成Unicode。那就是AllocSysString()和SetSysString()。除了SetSysString()使用BSTR*參數外,二者一樣。

    // 轉換成BSTR
    CString s5 = "Bob!";
    BSTR bs1 = NULL, bs2 = NULL;
    bs1 = s5.AllocSysString();
      s5.SetSysString ( &bs2 );
    // ...
      SysFreeString ( bs1 );
      SysFreeString ( bs2 );
    

    COleVariant 與CComVariant 非常相似。COleVariant 繼承于VARIANT,可以傳遞給需要VARIANT的函數。但又與CComVariant 不同,COleVariant 只有一個LPCTSTR的構造函數,不提供單獨的LPCSTR和LPCWSTR的構造函數。在大多情況下,沒有問題,因為總是愿意把字符串處理為LPCTSTR。但你必須知道這點。COleVariant 也有接受CString的構造函數。

    // 構造
    CString s1 = _T("tchar string");
    COleVariant v1 = _T("Bob"); // 從LPCTSTR構造
    COleVariant v2 = s1; // 從CString拷貝
    

    對于CComVariant,必須直接處理VARIANT成員,用ChangeType()方法在必要時將其轉換為字符串。但是,COleVariant::ChangeType() 在轉換失敗時會拋出異常,而不是返回HRESULT的出錯碼。

    // 數據萃取
    COleVariant v3 = ...; // 從某種類型構造v3
    BSTR bs = NULL;
    try
        {
        v3.ChangeType ( VT_BSTR );
        bs = v3.bstrVal;
        }
      catch ( COleException* e )
        {
        // 出錯,無法轉換
        }
    SysFreeString ( bs );
    

    WTL類

    WTL類

    CString

    WTL的CString與MFC的CString的行為完全相同,參閱上面關于MFC CString的說明即可。

    CLR 及 VC 7 類

    System::String 是.NET的字符串類。在其內部,String對象是一個不變的字符序列。任何操作String對象的String方法都返回一個新的String對象,因為原有的String對象要保持不變。String類有一個特性,當多個String都指向同一組字符集時,它們其實是指向同一個對象。Managed Extensions C++ 的字符串有一個新的前綴S,用來表明是一個managed string字符串。

    // 構造
    String* ms = S"This is a nice managed string";
    

    可以用unmanaged string字符串來構造String對象,但不如用managed string構造String對象有效。原因是所有相同的具有S前綴的字符串都指向同一個對象,而unmanaged string沒有這個特點。下面的例子可以說明得更清楚些:

    String* ms1 = S"this is nice";
    String* ms2 = S"this is nice";
    String* ms3 = L"this is nice";
    Console::WriteLine ( ms1 == ms2 ); // 輸出true
    Console::WriteLine ( ms1 == ms3);  // 輸出false
    

    要與沒有S前綴的字符串做比較,用String::CompareTo()方法來實現,如:

      Console::WriteLine ( ms1->CompareTo(ms2) );
      Console::WriteLine ( ms1->CompareTo(ms3) );
    

    二者都輸出0,說明字符串相等。

    在String和MFC 7的CString之間轉換很容易。CString可以轉換為LPCTSTR,String有接受char* 和 wchar_t* 的二種構造函數。因此可以直接把CString傳遞給String的構造函數:

      CString s1 ( "hello world" );
      String* s2 ( s1 );  // 從CString拷貝
    

    反向轉換的方法也類似:

      String* s1 = S"Three cats";
      CString s2 ( s1 );
    

    可能有點迷惑。從VS.NET開始,CString有一個接受String對象的構造函數,所以是正確的。

      CStringT ( System::String* pString );

    為了加速操作,有時可以用基礎字符串(underlying string):

    String* s1 = S"Three cats";
    Console::WriteLine ( s1 );
    const __wchar_t __pin* pstr = PtrToStringChars(s1);
    for ( int i = 0; i < wcslen(pstr); i++ )
        (*const_cast<__wchar_t*>(pstr+i))++;
    Console::WriteLine ( s1 );
    

    PtrToStringChars() 返回指向基礎字符串的 const __wchar_t* 指針,可以防止在操作字符串時,垃圾收集器去除該字符串。
    C++字符串完全指南(2) - 總結
    翻譯:連波
    23/11/2002
    URL: http://www.zdnet.com.cn/developer/tech/story/0,2000081602,39099061,00.htm

    字符串類的打印格式函數

    對字符串包裝類使用printf()或其它類似功能的函數時要特別小心。包括sprintf()函數及其變種,以及TRACE 和ATLTRACE 宏。它們的參數都不做類型檢驗,一定要給它們傳遞C語言字符串,而不是整個string對象。

    例如,要向ATLTRACE()傳遞一個_bstr_t 里的字符串,必須顯式用(LPCSTR)或 (LPCWSTR)進行強制類型轉換:


      _bstr_t bs = L"Bob!";
      ATLTRACE("The string is: %s in line %d\n", (LPCSTR) bs, nLine);
    

    如果忘了用強制類型轉換,直接把整個 _bstr_t 對象傳遞給ATLTRACE,跟蹤消息將輸出無意義的東西,因為_bstr_t 變量內的所有數據都進棧了。

    所有類的總結

    常用的字符串類之間的轉換方法是:將源字符串轉換為C類型字符串指針,然后將該指針傳遞給目標類的構造函數。下面列出將字符串轉換為C類型指針的方法,以及哪些類的構造函數接受C類型指針。

    Class

    string
    type

    convert to char*?

    convert to constchar*?

    convert to wchar_t*?

    convert to const wchar_t*?

    convert to BSTR?

    construct from char*?

    construct from wchar_t*?

    _bstr_t

    BSTR

    yes, cast1

    yes, cast

    yes, cast1

    yes, cast

    yes2

    yes

    yes

    _variant_t

    BSTR

    no

    no

    no

    cast to
    _bstr_t3

    cast to
    _bstr_t3

    yes

    yes

    string

    MBCS

    no

    yes, c_str()
    method

    no

    no

    no

    yes

    no

    wstring

    Unicode

    no

    no

    no

    yes, c_str()
    method

    no

    no

    yes

    CComBSTR

    BSTR

    no

    no

    no

    yes, cast
    to BSTR

    yes, cast

    yes

    yes

    CComVariant

    BSTR

    no

    no

    no

    yes4

    yes4

    yes

    yes

    CString

    TCHAR

    no6

    in MBCS
    builds, cast

    no6

    in Unicode
    builds, cast

    no5

    yes

    yes

    COleVariant

    BSTR

    no

    no

    no

    yes4

    yes4

    in MBCS builds

    in Unicode builds

    附注:

    1. 雖然 _bstr_t 可以轉換為非常量指針,但對內部緩沖區的修改可能導致內存溢出,或在釋放BSTR時導致內存泄露。
    2. bstr_t 的BSTR內含 wchar_t* 變量,所以可將const wchar_t* 轉換到BSTR。但這個用法將來可能會改變,使用時要小心。
    3. 如果轉換到BSTR失敗,將拋出異常。
    4. 用ChangeType()處理VARIANT的bstrVal。在MFC,轉換失敗將拋出異常。
    5. 雖然沒有BSTR的轉換函數,但AllocSysString()可返回一個新的BSTR。
    6. 用GetBuffer()方法可臨時得到一個非常量TCHAR指針。
    posted @ 2006-03-09 16:52 小鐵匠 閱讀(27914) | 評論 (3)編輯 收藏
    http://dev.csdn.net/article/82070.shtm
    http://blog.csdn.net/lxblg/archive/2004/09/14/104207.aspx
    posted @ 2006-03-09 15:08 小鐵匠 閱讀(409) | 評論 (0)編輯 收藏
         摘要: Iptables 指南 1.1.19 Oskar Andreasson      oan@frozentux.net     Copyright ? 2001-2003 by Oskar Andreasson 本文在符合 GNU Free Documentation 許可版本1.1的...  閱讀全文
    posted @ 2006-03-09 15:07 小鐵匠 閱讀(827) | 評論 (0)編輯 收藏

    啟動server時如果遇到找不到stub問題,原因是rmiregistry找不到stub,而不是java com.Server找不到stub,解決方法,在stub的類同一個目錄下啟動rmiregistry

    posted @ 2006-03-08 11:20 小鐵匠 閱讀(695) | 評論 (0)編輯 收藏
    問:我源文件為main.c, x.c, y.c, z.c,頭文件為x.h,y.h,z.h
    如何編譯成.so動態庫?
    編譯器用gcc
    最好能給出詳細參數解釋,謝謝

    答:
    # 聲稱動代連接庫,假設名稱為libtest.so
    gcc x.c y.c z.c -fPIC -shared -o libtest.so

    # 將main.c和動態連接庫進行連接生成可執行文件
    gcc main.c -L. -ltest -o main

    # 輸出LD_LIBRARY_PATH環境變量,一邊動態庫裝載器能夠找到需要的動態庫
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

    # 測試是否動態連接,如果列出libtest.so,那么應該是連接正常了
    ldd main

    # 執行就不用說了吧

    -fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯后的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正代碼段共享的目的。

    -L.:表示要連接的庫在當前目錄中

    -ltest:編譯器查找動態連接庫時有隱含的命名規則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱

    LD_LIBRARY_PATH:這個環境變量指示動態連接器可以裝載動態庫的路徑。
    當然如果有root權限的話,可以修改/etc/ld.so.conf文件,然后調用
    /sbin/ldconfig來達到同樣的目的,不過如果沒有root權限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
    posted @ 2006-03-03 16:20 小鐵匠 閱讀(5131) | 評論 (0)編輯 收藏

    keytool -genkey -dname "CN=demo, OU=softDept, O=company, L=puddong,S=shanghai, C=cn" -alias demo -keyalg RSA -keysize 1024 -keystore demoKeystore -validity 3650 -storepass storePwd -keypass demoPwd
    生成保存公鑰和私鑰的密鑰倉庫,保存在demoKeystore文件中。這里storepass 和 keypass 不要有java 正則表達式中的特殊字符,否則程序里要轉義麻煩。

    keytool -export -alias demo -keystore demoKeystore -rfc -file demo.cer //從密鑰倉庫中導出保存公鑰的證書
    輸入keypass 即demoPwd 


      try{     
       //密鑰倉庫
       KeyStore ks = KeyStore.getInstance("JKS");
    //讀取密鑰倉庫
       FileInputStream ksfis = new FileInputStream("demoKeystore");
       BufferedInputStream ksbufin = new BufferedInputStream(ksfis);
       char[] storePwd = "storePwd".toCharArray();
       ks.load(ksbufin, storePwd);
       ksbufin.close();
       char[] keyPwd = "demoPwd".toCharArray();
    //從密鑰倉庫得到私鑰
       PrivateKey priK = (PrivateKey) ks.getKey("demo", keyPwd);  
    //生成cipher
       Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding",new org.bouncycastle.jce.provider.BouncyCastleProvider());
    //用私鑰初始化cipher
       cipher.init(Cipher.ENCRYPT_MODE, priK);
       byte[] plain = "This is plain text".getBytes("UTF-8");
       
       //因為用的1024位rsa算法,一次只能加密1024/8-11字節數據,分開加密
       byte[] code = new byte[(((plain.length-1)/117+1))*128];  
                int ixplain = 0;
                int ixcode = 0;
                while((plain.length - ixplain) > 117) {//每117字節做一次加密
                    ixcode += cipher.doFinal(plain, ixplain, 117, code, ixcode);
                    ixplain += 117;
                }
                cipher.doFinal(plain, ixplain, plain.length - ixplain, code, ixcode);
                //加密后的code
                System.out.println(Arrays.toString(code));
                //通常會用base64編碼
               String base64 = encoder.encode(code);

       CertificateFactory certificatefactory = CertificateFactory
         .getInstance("X.509");
       //讀取證書
       FileInputStream fin = new FileInputStream("demo.cer");
       X509Certificate certificate = (X509Certificate) certificatefactory
         .generateCertificate(fin);
       fin.close();
       //得到公鑰
       PublicKey pubK = certificate.getPublicKey();
             //初始化cipher
                cipher.init(Cipher.DECRYPT_MODE, pubK);
          //base64解碼
                code = decoder.decodeBuffer(base64);
                System.out.println(Arrays.toString(code));
                byte[] plain2 = new byte[code.length];
                int ixplain2 = 0;
                int ixcode2 = 0;
                while((code.length - ixcode2) > 128) {//每128字節做一次解密
                    ixplain2 += cipher.doFinal(code, ixcode2, 128, plain2, ixplain2);
                    ixcode2 += 128;
                }
                ixplain2 += cipher.doFinal(code, ixcode2, code.length - ixcode2, plain2, ixplain2);
                String s2 = new String(plain2, 0, ixplain2, "UTF-8");
                System.out.println(s2);
       
      }catch(Exception ex){
       ex.printStackTrace();
      }

    keytool使用方法可以參考jdk文檔
    Java keytool工具的作用及使用方法

    posted @ 2006-03-02 14:32 小鐵匠 閱讀(3430) | 評論 (0)編輯 收藏
    在c++中new的對象,如果不返回java,必須用release掉,否則內存泄露。包括NewStringUTF,NewObject
    。如果返回java不必release,java會自己回收。

     jstring jstr = env->NewStringUTF((*p).sess_id);
       ...
     env->DeleteLocalRef( jstr);

    jobject jobj = env->NewObject(clazz,midInit);
    return jobj;

    內存泄露可以先從windows資源管理器中,看到隨程序運行,內存不斷增長的趨勢,具體可以用hp jmeter檢測在運行程序時,加jvm參數 -Xrunhprof:heap=all,cutoff=0 ,生成java.hprof.txt,用jmeter打開,Metric -> Residual Objects (Count),可以看到未回收的對象,選中要查看的對象,點Mark記錄下要查看的對象,Window -> New Window 打開新窗口,用Metric -> Reference Graph Tree,然后點Find Immediately可以看到對象被哪里引用。


    找出內存泄漏另一方法

    程序有內存泄漏的第一個跡象通常是它拋出一個 OutOfMemoryError,或者因為頻繁的垃圾收集而表現出糟糕的性能。幸運的是,垃圾收集可以提供能夠用來診斷內存泄漏的大量信息。如果以 -verbose:gc 或者 -Xloggc 選項調用 JVM,那么每次 GC 運行時在控制臺上或者日志文件中會打印出一個診斷信息,包括它所花費的時間、當前堆使用情況以及恢復了多少內存。記錄 GC 使用情況并不具有干擾性,因此如果需要分析內存問題或者調優垃圾收集器,在生產環境中默認啟用 GC 日志是值得的。

    有工具可以利用 GC 日志輸出并以圖形方式將它顯示出來,JTune 就是這樣的一種工具(請參閱 參考資料)。觀察 GC 之后堆大小的圖,可以看到程序內存使用的趨勢。對于大多數程序來說,可以將內存使用分為兩部分:baseline 使用和 current load 使用。對于服務器應用程序,baseline 使用就是應用程序在沒有任何負荷、但是已經準備好接受請求時的內存使用,current load 使用是在處理請求過程中使用的、但是在請求處理完成后會釋放的內存。只要負荷大體上是恒定的,應用程序通常會很快達到一個穩定的內存使用水平。如果在應用程序已經完成了其初始化并且負荷沒有增加的情況下,內存使用持續增加,那么程序就可能在處理前面的請求時保留了生成的對象。


    圖 1 顯示  GC 之后應用程序堆大小隨著時間的變化圖。上升趨勢是存在內存泄漏的警示信號。(在真實的應用程序中,坡度不會這么大,但是在收集了足夠長時間的 GC 數據后,上升趨勢通常會表現得很明顯。)


    圖 1. 持續上升的內存使用趨勢

    確信有了內存泄漏后,下一步就是找出哪種對象造成了這個問題。所有內存分析器都可以生成按照對象類進行分解的堆快照。有一些很好的商業堆分析工具,但是找出內存泄漏不一定要花錢買這些工具 —— 內置的 hprof 工具也可完成這項工作。要使用 hprof 并讓它跟蹤內存使用,需要以 -Xrunhprof:heap=sites 選項調用 JVM。

    清單 3 顯示分解了應用程序內存使用的 hprof 輸出的相關部分。(hprof 工具在應用程序退出時,或者用 kill -3 或在 Windows 中按 Ctrl+Break 時生成使用分解。)注意兩次快照相比,Map.EntryTaskint[] 對象有了顯著增加。

    請參閱 清單 3

    清單 4 展示了 hprof 輸出的另一部分,給出了 Map.Entry 對象的分配點的調用堆棧信息。這個輸出告訴我們哪些調用鏈生成了 Map.Entry 對象,并帶有一些程序分析,找出內存泄漏來源一般來說是相當容易的。


    清單 4. HPROF 輸出,顯示 Map.Entry 對象的分配點
    
    
    TRACE 300446:
    	java.util.HashMap$Entry.<init>(<Unknown Source>:Unknown line)
    	java.util.HashMap.addEntry(<Unknown Source>:Unknown line)
    	java.util.HashMap.put(<Unknown Source>:Unknown line)
    	java.util.Collections$SynchronizedMap.put(<Unknown Source>:Unknown line)
    	com.quiotix.dummy.MapLeaker.newTask(MapLeaker.java:48)
    	com.quiotix.dummy.MapLeaker.main(MapLeaker.java:64)
    




    另外
    jstring jstr = (jstring)env->CallObjectMethod(authenRequest, mid_authenReq_getSdId_S);
     env->GetStringUTFRegion(jstr,0,env->GetStringLength(jstr),authenReq.sd_id);
    當jstr是null時,env->GetStringLength(jstr)會出錯,導致jvm崩潰
    posted @ 2006-03-02 11:23 小鐵匠 閱讀(4241) | 評論 (0)編輯 收藏
    HP-UX下使用JNI訪問標準C++程序

    問題的關鍵在于用aCC編譯時的參數
    根據HP網站上的兩篇文章可以很容易的使用JNI訪問傳統C++(Classical C++)程序
    http://www.hp.com/products1/unix/java/infolibrary/prog_guide/JNI_java2.html 
    http://forums1.itrc.hp.com/service/forums/questionanswer.do?admit=716493758+1092296929165+28353475&threadId=245738 
    但是,如果代碼中使用到了標準C++,也就是用到了STL,就會出現莫名其妙的JVM crash. 而且一般的現象是使用string的時候出錯

    最后發現是JVM的多線程機制和aCC編譯的缺省的多線程機制不一樣.所以編譯時需要加參數指定
    總的說來,編譯參數為
    OPTS=-AA +z +u4 -D_RWSTD_MULTI_THREAD -D_REENTRANT -D_HPUX -D_HPUX_SOURCE -D_POSIX_C_SOURCE=199506L -D_XOPEN_SOURCE_EXTENDED 

    其中,-D_RWSTD_MULTI_THREAD -D_REENTRANT 是指定多線程機制;同時必須添加-D_HPUX_SOURCE 參數,否則,編譯時會出現奇怪的錯誤
    連接參數為
    -AA -b -lCsup_v2 -lstd_v2 
    值得注意的是根據上面所說的第二篇文章可知使用-AA編譯連接時,要連的庫是libCsup_v2.sllibstd_v2.sl(這兩個庫是支持標準C++的庫),而不是第一篇文章中提到的libCsup.sllibstd.sl(這兩個庫是支持傳統C++的庫). 

    另外,有幾個碰到的問題
    1. 
    如果編譯參數沒有指定多線程機制,禁用JIT(啟動JVM加參數:-Djava.compiler=none -Xint )可以使簡單的例子通過,但是有些情況下還是會出錯

    2. 
    null作為String傳入JNI native接口代碼中是,使用env->GetStringUTFChars(jstring)會出現如下錯誤導致虛擬機崩潰
    Function=verify_instance_jfieldID__18jfieldIDWorkaroundSFP12klassOopDescP9_jfieldID 

    3. 
    在使用String作為JNI的傳入傳出參數,使用GetStringUTFChars解決不了中文問題,還是會有亂碼正確的解決方法是使用以下兩個函數
    void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) 

        jclass cls = env->FindClass(name); 
        /* if cls is NULL, an exception has already been thrown */ 
        if (cls != NULL) { 
            env->ThrowNew(cls, msg); 
        } 
        /* free the local ref */ 
        env->DeleteLocalRef(cls); 


    jstring JNU_NewStringNative(JNIEnv *env, const char *str) 

      if (str==NULL) 
      { 
       return NULL; 
      } 
      jclass jcls_str = env->FindClass("java/lang/String"); 
      jmethodID jmethod_str = env->GetMethodID(jcls_str, "", "([B)V"); 

      jstring result; 
      jbyteArray bytes = 0; 
      int len; 

      if (env->EnsureLocalCapacity(2) < 0) { 
        return NULL; /* out of memory error */ 
      } 
      len = strlen(str); 
      bytes = env->NewByteArray(len); 
      if (bytes != NULL) { 
        env->SetByteArrayRegion(bytes, 0, len,(jbyte *)str); 
        result = (jstring)env->NewObject(jcls_str, jmethod_str, bytes); 
        env->DeleteLocalRef(bytes); 
        return result; 
      } /* else fall through */ 
      return NULL; 



    char *JNU_GetStringNativeChars(JNIEnv *env, jstring jstr) 

        jbyteArray bytes = 0; 
        jthrowable exc; 
        char *result = 0; 
        if (env->EnsureLocalCapacity(2) < 0) { 
            return 0; /* out of memory error */ 
        } 
    jclass jcls_str = env->FindClass("java/lang/String"); 
    jmethodID MID_String_getBytes = env->GetMethodID(jcls_str, "getBytes", "()[B"]; 

        bytes = (jbyteArray)env->CallObjectMethod(jstr, MID_String_getBytes); 
        exc = env->ExceptionOccurred(); 
        if (!exc) { 
            jint len = env->GetArrayLength( bytes); 
            result = (char *)malloc(len + 1); 
            if (result == 0) { 
                JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 
                                0); 
                env->DeleteLocalRef(bytes); 
                return 0; 
            } 
            env->GetByteArrayRegion(bytes, 0, len, (jbyte *)result); 
            result[len] = 0; /* NULL-terminate */ 
        } else { 
            env->DeleteLocalRef(exc); 
        } 
        env->DeleteLocalRef(bytes); 
        return (char*)result; 


    ★注意:使用char *JNU_GetStringNativeChars()獲得的指針用完后要顯式的free().

    posted @ 2006-02-27 15:54 小鐵匠 閱讀(978) | 評論 (0)編輯 收藏

    使用Axis傳送附件有兩種方式:

    1. 將你要傳送的文件封裝在DataHandler中,然后將DataHandler對象或DataHandler數組(多個文件傳送的時候)作為客戶端調用函數的參數(從客戶端上傳文件到服務器)Axis服務的返回類型(從服務器端下載文件到客戶端)進行傳輸。

    2. 還有一種方式是直接修改soap信封的內容,將附件信息加到soap信封中發送。

    這里我們只討論第一種方法,因為它實現起來非常簡單。關于第二種方法在Axis包的webapps/attachments/TestRf.java中有詳細的原碼可以參考。

    下面的例子是把文件從服務器端下載到客戶端:

    1.服務端程序:

    假設傳輸多個文件:在服務器端將文件取出來,并將文件封裝在DataHandler數組中。
    代碼如下:

     DataHandler[] ret = new DataHandler[totalFileNum];
     ... ...
     java.io.File myFile = new java.io.File(filePath);
     if(myFile.isFile() && myFile.canRead())
     {
      String fname = myFile.getAbsoluteFile().getCanonicalPath();
      DataHandler[0] = new DataHandler(new FileDataSource(fname));
     }
     ... ...

     return ret;

    上面的代碼將所有的文件封裝在了DataHandler數組中,并返回。

    2. 客戶端的訪問:

    代碼如下:
     Service service = new Service();
     Call call = (Call) service.createCall();

     URL myURL = new URL(" call.setTargetEndpointAddress(myURL); //設定服務的主機和位置
     call.setOperationName(new QName("urn:MyAttachServer","echoDir")); //設置要調用的服務的方法
     QName qnameAttachment = new QName("urn:MyAttachServer","DataHandler");

     call.registerTypeMapping(DataHandler.class, qnameAttachment, JAFDataHandlerSerializerFactory.class,JAFDataHandlerDeserializerFactory.class); //為附件(即DataHandler類)創建序列化生成器

     call.addParameter("source", XMLType.XSD_STRING ,ParameterMode.IN); //設置服務調用方法的傳入參數類型
     call.setReturnType(XMLType.SOAP_ARRAY); //設置調用服務方法的返回類型,由于返回的是DataHandler數組,所以設置為SOAP_ARRAY類型
     javax.activation.DataHandler[] ret = (javax.activation.DataHandler[])call.invoke(new Object[]{null}); //調用方法

     for (i = 0; i < ret.length; ++i)
            {
                DataHandler recDH = ret[i];
                java.io.File receivedFile = new java.io.File(recDH.getName()); //文件生成
            }

    3. 服務的部署:

    注意:你要在部署的時候,定義DataHandler的序列化生成器。

      編寫deploy.wsdd文件:

     <deployment xmlns="  <service name="urn:att_STC_Server" provider="java:RPC" >
        <parameter name="className" value="samples.att_STC.att_STC_Server"/>
        <parameter name="allowedMethods" value="echoDir"/>

     <typeMapping deserializer="org.apache.axis.encoding.ser.JAFDataHandlerDeserializerFactory"
       languageSpecificType="java:javax.activation.DataHandler" qname="ns1:DataHandler"
        serializer="org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory"
        encodingStyle="
    http://schemas.xmlsoap.org/soap/encoding/"/>
      </service>

    </deployment>

    運行java org.apache.axis.client.AdminClient %* deploy.wsdd,部署服務。

    posted @ 2006-02-20 16:28 小鐵匠 閱讀(1035) | 評論 (1)編輯 收藏

    2004-05-29 17:39:53

    主題: 使用Java實現CA(四)

        前面幾篇文章已經把如何用Java實現一個CA基本上講完了.但是他們都有一個特點,就是用戶的信息都是在現場獲取的,不能做申請和簽發相分離.今天我們要講述的是PKCS#10證書請求文件.它的作用就是可以使申請和簽發相分離.

        PKCS#10證書請求結構中的主要信息包含了被簽發者(證書申請者)的主體名稱(DN)和他的公鑰.因此一個CA在獲取到一個PKCS#10證書請求后,就可以從中獲取到任何和簽發證書有關的信息,然后用它自己的私鑰簽發證書.

        使用BC Provider在Java中構造一個證書請求格式的對象調用其構造函數即可,這個函數如下:

        PKCS10CertificationRequest(java.lang.String signatureAlgorithm, X509Name subject, java.security.PublicKey key, ASN1Set attributes, java.security.PrivateKey signingKey)

        它的參數是自簽名算法,證書申請者的DN,證書申請者的公鑰,額外的屬性集(就是要申請的證書的擴展信息),申請證書者的私鑰.申請證書者的私鑰僅僅是用來進行一下自簽名,并不出現在證書請求中,需要自簽名的目的是保證該公鑰確實為申請者所有.

        調用該對象的getEncoded()方法可以將其進行DER編碼,然后儲存起來,該對象還有另一個構造函數:
        PKCS10CertificationRequest(byte[] bytes)
        這個構造函數的作用就是直接從儲存的DER編碼中把這個對象還原出來.

        利用證書請求結構進行證書簽發的代碼如下,這里假設CSR是一個已經獲取出來的PKCS10CertificationRequest結構:

        PublicKey SubjectPublicKey = CSR.getPublicKey();
        CertificationRequestInfo CSRInfo = CSR.getCertificationRequestInfo();
        X509Name SubjectDN = CSRInfo.getSubject();
        ASN1Set Attributes = CSRInfo.getAttributes();

        這樣,申請者的主體DN,申請者的公鑰,申請者希望在證書擴展信息中填寫的屬性都得到了,剩下的事情就和用戶在現場輸入時一樣了,其它的信息一般是申請者不能決定的.另外證書請求格式中有一樣信息沒有明確給出來,那就是證書的有效期,這個應該單獨詢問用戶,或者用其它的方法保存起來.



    [返回頂部]


    2004-05-28 16:46:12

    主題: 使用Java實現CA(三)

        前幾次我已經基本上把如何做CA所需要的基礎知識講得差不多了,今天直接講如何用Java程序來實現一個CA應該就不是什么太困難的事情了.

        要做CA,第一步要準備好自己的證書和私鑰.私鑰如何從文件里面讀取出來前面已經講過了.從文件系統中讀出證書的代碼如下:

        CertificateFactory certCF = CertificateFactory.getInstance("X.509");
        X509Certificate caCert = certCF.generateCertificate(certBIS);

        這里cerBIS是一個InputStream類型的對象.例如一個標準的X509v3格式的證書文件所形成的輸入流.

        第二步就是從用戶那里獲取輸入,然后構造主體名稱結構DN,如何構造DN上次已經說過了,如何從用戶那里獲取輸入,這個不在本文討論范圍之內.

        下一步就是獲取用戶的公鑰,好和他所需要的證書對應起來.也有不少CA的做法就是在這里替用戶現場生成一對密鑰對,然后把公鑰放到證書中簽發給用戶.這個應該看實際需要選擇合適的方式.

        現在一切信息都已經準備好了,可以簽發證書了,下面的代碼說明了這個過程:

        //構造一個證書生成器對象

        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();

        // 從CA的證書中獲取簽發者的主體名稱(DN)
        // 這里有一點小技巧,我們要把JCE中定義的
        // 用來表示DN的對象X500Principal轉化成在
        // BC Provider中的相應的對象X509Name
        // 先從CA的證書中讀取出CA的DN進行DER編碼
        DERInputStream dnStream =
                     new DERInputStream(
          new ByteArrayInputStream(
           caCert.getSubjectX500Principal().
            getEncoded()));
        // 馬上又從編碼后的字節流中讀取DER編碼對象
        DERConstructedSequence  dnSequence =
         (DERConstructedSequence)dnStream.readObject();
        // 利用讀取出來的DER編碼對象創建X509Name
        // 對象,并設置為證書生成器中的"簽發者DN"
        certGen.setIssuerDN(new X509Name(dnSequence));
        // 設置好證書生成器中的"接收方DN"
        certGen.setSubjectDN(subjectDN);
        // 設置好一些擴展字段,包括簽發者和
        // 接收者的公鑰標識
        certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
        createSubjectKeyId(keyToCertify));
        certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
        createAuthorityKeyId(caCert.getPublicKey()));
        // 設置證書的有效期和序列號
        certGen.setNotBefore(startDate);
        certGen.setNotAfter(endDate);
        certGen.setSerialNumber(serialNumber);
        // 設置簽名算法,本例中使用MD5hash后RSA
        // 簽名,并且設置好主體的公鑰
        certGen.setSignatureAlgorithm("MD5withRSA");
        certGen.setPublicKey(keyToCertify);

        // 如果以上一切都正常地話,就可以生成證書了
        X509Certificate cert = null;
        cert = certGen.generateX509Certificate(caPrivateKey);

        這里是上面用到的生成簽發者公鑰標識的函數: 

        protected AuthorityKeyIdentifier createAuthorityKeyId(PublicKey pubKey)
        {
        AuthorityKeyIdentifier authKeyId = null;

        try
        {
        ByteArrayInputStream bIn = new ByteArrayInputStream(pubKey.getEncoded());
        SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(
            (DERConstructedSequence)new DERInputStream(bIn).readObject());
        authKeyId = new AuthorityKeyIdentifier(info);
        }
        catch (IOException e)
        {
        System.err.println("Error generating SubjectKeyIdentifier:  " +
            e.toString());
        System.exit(1);
        }

        return authKeyId;
        }

        生成主體公鑰標識的函數和上面的類似,把AuthorityKeyIdentifier替換成SubjectKeyIdentifier就可以了.

        這里要注意的是,CA的公鑰也是在一份證書里,這種證書的特點是簽發者DN和接收者DN一樣,也就是說,這種證書是CA自己給自己頒發的證書,也就是"自簽名證書",它上面的公鑰是CA自身的公鑰,用來簽名的私鑰就是該公鑰對應的私鑰.一般每個CA都要有這么一份證書,除非該CA不是根CA,即它的權威性不是由它自己證明,而是由它的上級CA證明.但是,最后總歸要有一個根CA,它為各個安全應用程序的用戶所信賴.

        到這里我們已經把CA最基本的功能如何用Java實現講完了,下一次講如何從PKCS#10格式證書請求文件中讀取出用戶信息,然后直接簽發公鑰.



    [返回頂部]


    2004-05-27 15:34:59

    主題: 使用Java實現CA(二)

        昨天本來快寫完了,結果不小心按了"tab"鍵,然后向按退格鍵,結果退到前一個頁面了,然后全部都白寫了,不爽.只好今天重新寫了.

        上次我們講到如何生成密鑰對,以及如何將諸如公鑰,私鑰,證書等這一類安全對象在文件系統和內存之間來回轉換.這些是準備開CA的基本功,今天我們講一下CA的基本原理以及如何使用主體名稱結構DN(Distinguish Name)來表示每一個證書中的主體.

        一份證書就是一個權威機構對一個主體的身份的確認的證明.即一份證書表示一個權威機構確認了一個主體就是它自己,而不是其它的冒名頂替者.主體可以是一個個人,也可以不是,例如,在需要安全服務的時候,需要為一臺網站的服務器頒發證書,這種證書的主體就是一臺服務器.簽署證書的權威機構就叫做CA,該權威機構本身也是一個主體.權威機構通過對包含有待認證的主體的一系列信息的待簽名證書"(TBS,to be signed)進行數字簽名來表示該機構對它的認可.一份包含了待認證的主體的相關信息的TBS再加上CA使用自己的私鑰進行簽名產生的字節流放在一起,就構成了一份標準的X509證書.

        一個TBS中包含了以下這些主要信息:

        證書的版本,通常是3(X509v3)

        證書的序列號,RFC3280中規定,每個CA必須確保它頒發的每一份證書的序列號都是唯一的,并且序列號只能使用非負整數.

        簽發者(CA)的主體名稱,一個DN對象.

        證書的有效期,表示方法是兩個時間值,表示了該證書從何時開始生效,何時開始作廢.

        待認證的主體的主體名稱,也是一個DN對象.

        待認證的主體的公鑰,任何安全應用在確認完證書的有效性后,就可以開始使用該主體的公鑰與之進行安全通信.

        如果是X509v3證書,即版本號是3的話,后面還有一個證書擴展信息字段,可以在證書里面添加一些其它的信息.

        下面我們來看一下表示主體的主體名稱結構:DN.這個結就構是一個屬性的集合.每個屬性有屬性名稱和屬性值.它的作用就是用來表示"我是誰",也就是說,這個證書到底是誰頒發給誰的,這個證書對應的公鑰是誰擁有的.

        通常使用一個字符串來表示DN結構,這種字符串說明了這種結構中的各個屬性的類型和值:

        C=CN;S=BeiJing;L=BeiJing;O=PKU;OU=ICST;CN=wolfenstein

        這里C是國家和地區代碼,S和L都是地區代碼,S相當于省或者州這樣的級別,L相當于城市級別,O是組織機構名稱,OU是次級組織機構名稱,CN是主體的通用名(common name).在這里,C,S,L等等屬性的類型都是相對固定的,例如C一般就是用來表示國家和地區代碼,在DN結構中還可以添加一些其它類型的信息,一般也都是以"xxx=xxx"這樣來表示的.

        下面我們來說明如何在Java語言中構造出一個主體名稱對象.

        BC Provider中使用X509Name對象來表示DN,構造一個X509Name的步驟大體如下:

        先構造兩個vector對象,用來表示屬性名稱和屬性值:

        Vector oids = new Vector();
        Vector attributes = new Vector();

        然后在oids這個用來表示屬性名稱的vector對象中將屬性名稱一個一個添加進去:

        oids.addElement(X509Name.C);

        ......

        oids.addElement(X509Name.CN);

        X509Name對象里面有若干常量,例如上面的X509Name.C.還有X509Name.ST等等,都可以和上面舉的例子對應起來.

        然后將屬性值添加到attributes對象中:

        attributes.addElement("CN");

        ......

        attributes.addElement("Wolfenstein");

        最后就可以構造出一個X509Name對象:

        X509Name SubjectDN = new X509Name(oids, attributes);

        這樣,我們的主體名稱結構就確立起來了.

        下次我們就可以講關鍵的部分了,那就是如何用Java程序完成CA最重要的功能,簽署證書.



    [返回頂部]


    2004-05-25 21:23:52

    主題: 使用Java實現CA(一)

        通過我昨天的文章大家應該已經清楚了,用Java寫信息安全方面的程序需要做的準備工作.好了,現在假設你已經是一個對Java語言本身比較熟悉,能夠用Java寫一些程序的人,并且這些該下載的jar包已經下載好了,可以正式開工了.

        在所有的此類程序的開頭(無論是在類的構造函數中也好,在初始化函數中也好),都要先來上這么一句:Security.addProvider(new BouncyCastleProvider());將BouncyCaslte的Provider添加到系統中,這樣以后系統在運行相關程序的時候調用的就是這個Provider中的加密算法.

        然后我們就可以開始開CA了.首先,作為一個CA要有自己的一對公鑰和私鑰,我們先要生成這么一對.使用KeyPairGenerator對象就可以了,調用KeyPairGenerator.getInstance方法可以根據要生成的密鑰類型來產生一個合適的實例,例如常用的RSA,DSA等.然后調用該對象的initialize方法和generateKeyPair方法就可以產生一個KeyPair對象了.然后調用KeyPair對象中的相應方法就可以獲取生成的密鑰對中的公鑰和私鑰了.

        有了公鑰和私鑰對以后,下面的一個很現實問題就是如何把它們儲存起來.通常我們要對這些安全對象,如公鑰,私鑰,證書等先進行編碼.編碼的目的是為了把結構復雜的安全對象變成字節流以便存儲和讀取,如DER編碼.另外,通常還把DER編碼后的字節流再次進行base64編碼,以便使字節流中所有的字節都變成可打印的字節.

        在Java語言中,這些安全對象基本上都有getEncoded()方法.例如:

        byte[] keyBytes = privateKey.getEncoded();

        這樣就把一個私鑰進行了DER編碼后的結果保存到一個byte數組中了.然后就可以把這個byte數組保存到任何介質中.如果有必要的話,可以使用BC Provider中的Base64編碼解碼器類進行編碼,就像這樣:

        byte data[] = Base64.encode(keyBytes);

        要從文件中讀取私鑰則應該這樣:

        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyData);
        KeyFactory kfac = KeyFactory.getInstance("RSA");
        privateKey = kfac.generatePrivate(spec);

        這里說明一下,對RSA私鑰進行編碼就會自動地按照PKCS8進行.因此讀取的時候將包含編碼的字節數組作為PKCS8EncodedKeySpec對象的構造函數的參數就可以生成一個該類型的對象.然后創建一個密鑰工廠對象就可以按照要求生成一個RSA私鑰了.很顯然這里的keyData應該是和上面的keyBytes內容相同.

        為了提高系統的安全性,通常私鑰在經過DER編碼后,還會使用一個口令進行加密,然后再儲存在文件系統中.在使用私鑰的時候,如果沒有正確的口令,是無法把私鑰還原出來的.

        保存證書和保存私鑰類似.Certificate對象中也有一個getEncoded的方法.

        這次就講這些.大家應該可以把這些安全對象很熟練地從文件系統和內存之間來回地折騰了吧.這對以后實現CA是很重要的.下次我會講一下證書中表示主體的方法:DN.



    [返回頂部]


    2004-05-24 17:23:06

    主題: 使用Java開發和信息安全相關的程序

        這里說的信息安全是相對于系統安全而言的,它更側重于加密,解密,數字簽名,驗證,證書等等.而系統安全主要側重于系統本身是否有安全漏洞,如常見的由于軟件設計的不完善而導致的滿天飛的緩沖區溢出等等.

        Java語言中負責加密功能的部件是JCE(Java Crypto Extenstion),它使用開放式的思想,可以允許用戶自己編寫加密算法的具體實現的模塊等.這些東西被稱為JCE Provider,JCE的提供者.SUN公司本身提供了一些Provider.不過我推薦使用Bouncy Castle的Provider.原因是它實現的加密算法多,連比較新的橢圓曲線(ECC)算法都有了.去http://www.bouncycastle.org/可以找到你所希望的.Bouncy Castle除了提供Provider本身以外,還包括了一個S/MIME和一個open pgp 的jar包只有Provider本身是必要的,后兩個包是方便你編程而提供的.例如有了S/MIME包后,你就不再需要為諸如"加密一個字符串或者一片文章然后簽名"之類的很現實的應用程序寫上一大堆代碼了,只要引用S/MIME包中的某幾個類,很快就可以搞定.而open pgp的存在,使你在用Java編寫和PGP/GPG交互的程序時方便多了.

        這次先寫這么多了,下次開始具體講把這些東西搞下來后怎么開始干活吧.我以我現在手頭上正在做的事情告訴大家,如何做一個CA.

        有了BC Provider,開CA,真的就是這么簡單!

    posted @ 2006-02-20 11:07 小鐵匠 閱讀(6110) | 評論 (0)編輯 收藏
    jdbc url:jdbc:oracle:thin:@(DESCRIPTION=(LOAD_BALANCE=on)(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.0.1)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.0.2)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=sa)))
    其他照常
    posted @ 2005-12-14 10:12 小鐵匠 閱讀(502) | 評論 (0)編輯 收藏
    主站蜘蛛池模板: 免费人成在线观看视频高潮| 免费欧洲美女牲交视频| 一区在线免费观看| 国产.亚洲.欧洲在线| 亚洲av永久无码制服河南实里| 在线观看免费成人| 97免费人妻无码视频| 东方aⅴ免费观看久久av | 中文字字幕在线高清免费电影| 在线亚洲午夜片AV大片| 久久精品蜜芽亚洲国产AV| 国精无码欧精品亚洲一区| 亚洲日韩国产成网在线观看| 青青草国产免费久久久下载| 中文字幕av无码无卡免费| 中文字幕免费在线观看| 久久精品国产影库免费看| 国产在线国偷精品免费看| 亚洲精品视频免费| 日本中文字幕免费看| 国产亚洲精品第一综合| 国产成人亚洲精品蜜芽影院| 色婷婷六月亚洲综合香蕉| 亚洲精品色播一区二区 | 毛片a级三毛片免费播放| 成人黄色免费网站| 91网站免费观看| 国产四虎免费精品视频| 久久久久久久免费视频| 91成年人免费视频| 99久久免费国产精品特黄| 免费毛片在线看片免费丝瓜视频| 麻豆国产精品免费视频| 很黄很色很刺激的视频免费| 日本亚洲免费无线码| 无限动漫网在线观看免费| 午夜两性色视频免费网站| 国产成人精品男人免费| mm1313亚洲国产精品美女| 亚洲天堂在线视频| 亚洲AV中文无码字幕色三|