・ 日本語(2バイト文字)の処理

レガシーな Auto Lisp の文字列処理関数は、ちょっと寂しい。
(strlen) (substr) くらいしかないのです。
しかも日本語(2バイト文字)には未対応。
少しはまともになった Vuisual Lisp でも、日本語(2バイト文字)は NG です。・・・・(~_~;)

「ならば、作ってしまえ。」ということで調べてみると。
1バイト文字(半角文字)の文字コードは、32〜126 と 161〜223 です。
(ascii)使って、1バイトと2バイトを区別すればいいわけですね。

(defun Jof_chk_cbyte(   ;文字列の先頭が半角なら1をそうでないなら2を返す;
		         str ;文字列;
		         / a )
  (setq a (ascii str))
  (if (or (and (> a 31) (< a 127)) (and (> a 160) (< a 224))) 1 2)
  )
  

ところが、これを使って、文字列処理の関数を作ろうと思うと、頭がこんがらかって 「ホヘっ?」 です。
なんたって、一文字ずつ1バイトか2バイトか考えないといけないわけですから・・・・(-_-;)

そこで思いついたのが、「文字列を一文字ずつのリストにしてから処理したら」ってことです。
我ながら名案  (^_^)v
「多少遅くなろうが、動けばいいのさ。」

(defun Jof_str2clst(    ;文字列を一文字ごとアトムにしてリスト化;
		        str ;文字列;
		       / byte lst_str)
  (while (not (= str ""))
    (setq byte (Jof_chk_cbyte str))
    (cond
      ((= byte 1)
       (setq lst_str (cons (substr str 1 1) lst_str))
       (setq str (substr str 2)))
      ((= byte 2)
       (setq lst_str (cons (substr str 1 2) lst_str))
       (setq str (substr str 3)))
      )
    )
  (reverse lst_str)
  )
(defun Jof_clst2str(        ;文字のリストを文字列に変換;
		       lst_str ;文字のリスト;
		       / )
  (apply 'strcat lst_str)
  )

あとは、リスト処理の関数使えば、いろいろできそうです。

というわけで、作った文字列処理関数セット (Jo_str_set.lsp) を、お蔵入りツールにもアップしときました。
すべて2バイト文字対応です。
これだけでは、コマンドとして利用はできませんが、他のプログラムからコールするファンクションとしてご利用ください。

;***********************************************************************;
;文字列処理関数セット  2バイト文字対応                                  ;
;               更新履歴                                                ;
;                 2004/07/11 Jof_instr2 のバグフィックス                ;
;                 2004/07/12 Jof_bsubstr2 の追加                        ;
;                                                2004/06/23  by Kamijo  ;
;***********************************************************************;

;///////////////////////////////////////////////////////////////////////;
;文字列処理関数サブセット                                               ;
;///////////////////////////////////////////////////////////////////////;

;***********************************************************************;
;文字列の先頭が半角なら1をそうでないなら2を返す                         ;
;***********************************************************************;
(defun Jof_chk_cbyte(    
		     str ;文字列;
		     / a )
  (setq a (ascii str))
  (if (or (and (> a 31) (< a 127)) (and (> a 160) (< a 224))) 1 2)
  )

;***********************************************************************;
;文字列を一文字ごとアトムにしてリスト化                                 ;
;***********************************************************************;
(defun Jof_str2clst(    
		    str ;文字列;
		    / byte lst_str)
  (while (not (= str ""))
    (setq byte (Jof_chk_cbyte str))
    (cond
      ((= byte 1)
       (setq lst_str (cons (substr str 1 1) lst_str))
       (setq str (substr str 2)))
      ((= byte 2)
       (setq lst_str (cons (substr str 1 2) lst_str))
       (setq str (substr str 3)))
      )
    )
  (reverse lst_str)
  )

;***********************************************************************;
;文字のリストを文字列に変換                                             ;
;***********************************************************************;
(defun Jof_clst2str(       
		   lst_str ;文字のリスト;
		   / )
  (apply 'strcat lst_str)
  )

;***********************************************************************;
;リストのn番目からx個の要素をリストで返す                               ;
;***********************************************************************;
(defun Jof_lstmid(    
		  lst ;リスト;
		  n   ;n番目から 先頭は0番目;
		  x   ;x個;
		  / len ret)
  (setq len (length lst))
  (repeat (min n len)
    (setq lst (cdr lst))
    )
  (setq len (length lst))
  (repeat (min x len)
    (setq ret (cons (car lst) ret))
    (setq lst (cdr lst))
    )
  (reverse ret)
  )

;***********************************************************************;
;リストのn番目からx個の要素を削除して返す                               ;
;***********************************************************************;
(defun Jof_lstmid_del(   
		     lst ;リスト;
		     n   ;n番目から 先頭は0番目;
		     x   ;x個;
		     / len ret)
  (setq len (length lst))
  (repeat (min n len)
    (setq ret (cons (car lst) ret))
    (setq lst (cdr lst))
    )
  (setq len (length lst))
  (repeat (min x len)
    (setq lst (cdr lst))
    )
  (setq len (length lst))
  (repeat len
    (setq ret (cons (car lst) ret))
    (setq lst (cdr lst))
    )
  (reverse ret)
  )

;***********************************************************************;
;リストをn番目より前と以後にわけて返す                                  ;
;***********************************************************************;
(defun Jof_lst_div(    
		   lst ;リスト;
		   n   ;n番目 先頭は0番目;
		   / len ret1 ret2 )
  (setq len (length lst))
  (repeat (min n len)
    (setq ret1 (cons (car lst) ret1))
    (setq lst (cdr lst))
    )
  (setq len (length lst))
  (repeat len
    (setq ret2 (cons (car lst) ret2))
    (setq lst (cdr lst))
    )
  (list (reverse ret1) (reverse ret2))
  )  
  
;///////////////////////////////////////////////////////////////////////;
;以下ユーザーがコールするファンクション                                 ;
;(サブセット以外のファンクションをコールしているものもあるので注意)   ;
;///////////////////////////////////////////////////////////////////////;

;***********************************************************************;
;文字列の長さを返す                                                     ;
; (strlen の2バイト文字対応版)                                        ;
;***********************************************************************;
(defun Jof_strlen2(    
		   str ;文字列;
		   / )
  (length (Jof_str2clst str))
  )

;***********************************************************************;
;文字列の左から指定した文字数分の文字列を返す                           ;
;  (VBAの Left みたいなのです)                                     ;
;***********************************************************************;
(defun Jof_strleft2(    
		    str ;文字列;
		    len ;指定した文字数;
		    / lst_str)
  (setq lst_str (Jof_str2clst str))
  (setq lst_str (Jof_lstmid lst_str 0 len))
  (Jof_clst2str lst_str)
  )

;***********************************************************************;
;文字列の右から指定した文字数分の文字列を返す                           ;
;  (VBAの Right みたいなのです)                                    ;
;***********************************************************************;
(defun Jof_strright2(    
		     str ;文字列;
		     len ;指定した文字数;
		     / lst_str)
  (setq lst_str (reverse (Jof_str2clst str)))
  (setq lst_str (reverse (Jof_lstmid lst_str 0 len)))
  (Jof_clst2str lst_str)
  )  

;***********************************************************************;
;文字列の指定した位置から指定した文字数分の文字列を返す                 ;
;  (substr の2バイト文字対応版)                                       ;
;***********************************************************************;
(defun Jof_substr2(      
		   str   ;文字列;
		   start ;指定した位置 先頭は1番目;
		   len   ;指定した文字数;
		   / lst_str)
  (setq lst_str (Jof_str2clst str))
  (setq lst_str (Jof_lstmid lst_str (1- start) len))
  (Jof_clst2str lst_str)
  )

;***********************************************************************;
;文字列を検索し、その最初の文字位置を返す ない場合はnil  先頭は1番目    ;
;  (VBAの InStr みたいなのです)                                    ;
;***********************************************************************;
(defun Jof_instr2(      
		  start ;検索スタート位置 先頭は1番目;
		  str1  ;対象文字列;
		  str2  ;検索文字列;
		  / lst_str1 lst_str1d lst_str1dd lst_str2 lst_str2d len flg)
  (setq lst_str1 (reverse (Jof_str2clst str1)))
  (setq len (- (length lst_str1) (1- start)))
  (setq lst_str1d (reverse (Jof_lstmid lst_str1 0 len)))  
  (setq lst_str2 (Jof_str2clst str2))
  (setq start nil)
  (while (and lst_str1d (not start))
    (setq lst_str1d (member (car lst_str2) lst_str1d))
    (setq flg (if (>= (length lst_str1d) (length lst_str2)) T nil))
    (setq lst_str1dd (setq lst_str1d (cdr lst_str1d)))
    (setq lst_str2d (cdr lst_str2))    
    (while (and flg lst_str2d)
      (if (= (car lst_str1dd) (car lst_str2d))
	(progn
	  (setq lst_str1dd (cdr lst_str1dd))
	  (setq lst_str2d (cdr lst_str2d))
	  )
	(setq flg nil)
	)
      )
    (setq start (if flg (1+ (- (length lst_str1) (+ (length lst_str1dd) (length lst_str2)))) nil))
    )
  start
  )

;***********************************************************************;
;文字列を検索し、その文字列を削除して返す                               ;
;***********************************************************************;
(defun Jof_instr_del2(      
		      start ;検索スタート位置 先頭は1番目;
		      str1  ;対象文字列;
		      str2  ;検索文字列;
		      multi ;連続実行フラグ nil:1回 / T:繰返し実行;
		      / lst_str n x ret )
  (setq n (Jof_instr2 start str1 str2))
  (setq lst_str (Jof_str2clst str1))
  (setq x (Jof_strlen2 str2))
  (if (and n x)
    (progn
      (setq lst_str (Jof_lstmid_del lst_str (1- n) x))
      (setq ret (Jof_clst2str lst_str))
      (if multi
	(Jof_instr_del2 start ret str2 T)
	ret
        )
      )
    str1
    )  
  )

;***********************************************************************;
;文字列の指定した位置から指定した文字数分削除して返す                   ;
;***********************************************************************;
(defun Jof_substr_del2(      
		       str   ;対象文字列;
		       start ;指定した位置  先頭は1番目;
		       len   ;指定した文字数;
		       / lst_str)
  (setq lst_str (Jof_str2clst str))
  (setq lst_str (Jof_lstmid_del lst_str (1- start) len))
  (Jof_clst2str lst_str)
  )

;***********************************************************************;
;文字列を検索し、その文字列を置き換える                                 ;
;***********************************************************************;
(defun Jof_instr_chg2(      
		      start ;検索スタート位置 先頭は1番目;
		      str1  ;対象文字列;
		      str2  ;検索文字列;
		      str3  ;置換文字列;
		      multi ;連続実行フラグ nil:1回 / T:繰返し実行;
		      / n x f lst_str ret)
  (setq n (Jof_instr2 start str1 str2))
  (setq lst_str (Jof_str2clst str1))
  (setq x (Jof_strlen2 str2))
  (setq f (if (= str2 str3) nil t))  
  (if (and n x f)
    (progn
      (setq lst_str (Jof_lstmid_del lst_str (1- n) x))
      (setq lst_str (Jof_lst_div lst_str (1- n)))
      (setq ret (strcat (Jof_clst2str (car lst_str)) str3 (Jof_clst2str (cadr lst_str))))
      (if multi
	(Jof_instr_chg2 start ret str2 str3 T)
	ret
	)
      )
    str1
    )  
  )

;***********************************************************************;
;文字列の指定した位置から指定した文字数分削除し、文字列を置き換える     ;
;  (VBAの Mid みたいなのです)                                      ;
;***********************************************************************;
(defun Jof_substr_chg2(      
		       str1  ;対象文字列;
		       start ;指定した位置  先頭は1番目;
		       len   ;指定した文字数;
		       str2  ;置換文字列;
		      / n  x lst_str )
  (setq lst_str (Jof_str2clst str1))
  (setq lst_str (Jof_lstmid_del lst_str (1- start) len))
  (setq lst_str (Jof_lst_div lst_str (1- start)))
  (strcat (Jof_clst2str (car lst_str)) str2 (Jof_clst2str (cadr lst_str)))
  )	

;***********************************************************************;
;文字先頭の空白を削除                                                   ;
;  (VBAの LTrim みたいなのです)                                    ;
;***********************************************************************;
(defun Jof_strtrim_L2(     
		       str ;対象文字列;
		       / lst_str)
  (setq lst_str (Jof_str2clst str))
  (while (or (= (car lst_str) " ") (= (car lst_str) " "))
    (setq lst_str (cdr lst_str))
    )
  (Jof_clst2str lst_str)
  )

;***********************************************************************;
;文字末尾の空白を削除                                                   ;
;  (VBAの RTrim みたいなのです)                                    ;
;***********************************************************************;
(defun Jof_strtrim_R2(     
		       str ;対象文字列;
		       / lst_str)
  (setq lst_str (Jof_str2clst str))
  (setq lst_str (reverse lst_str))
  (while (or (= (car lst_str) " ") (= (car lst_str) " "))
    (setq lst_str (cdr lst_str))
    )
  (setq lst_str (reverse lst_str))
  (Jof_clst2str lst_str)
  )

;***********************************************************************;
;文字前後の空白を削除                                                   ;
;  (VBAの Trim みたいなのです)                                     ;
;***********************************************************************;
(defun Jof_strtrim2(    
		    str ;対象文字列;
		    / )
  (Jof_strtrim_R2 (Jof_strtrim_L2 str))
  )

;***********************************************************************;
;文字列を指定した区切り文字で分割し、リストにする                       ;
;***********************************************************************;
(defun Jof_str2slst2(    
		     str ;対象文字列;
		     cha ;区切り文字;
		     / lst_str lst_ret lst_a)
  (setq lst_str (Jof_str2clst str))
  (while lst_str
    (setq lst_a nil)
    (while (and lst_str (not (= cha (car lst_str))))
      (setq lst_a (cons (car lst_str) lst_a))
      (setq lst_str (cdr lst_str))
      )
    (setq lst_ret (cons (Jof_clst2str (reverse lst_a)) lst_ret))
    (setq lst_str (cdr lst_str))
    )
  (reverse lst_ret)      
  )

;***********************************************************************;
;文字列のリストを指定した区切り文字でつないで文字列にする               ;
;***********************************************************************;
(defun Jof_slst2str2(    
		     lst ;対象リスト;
		     cha ;区切り文字;
		     / flg str_ret)
  (setq flg 1)
  (foreach n lst (if (/= 'STR (type n)) (setq flg 0)))
  (if (= flg 1)
    (progn
      (setq str_ret (car lst))
      (foreach n (cdr lst) (setq str_ret (strcat str_ret cha n)))
      )
    (princ "\nリストの中に文字列以外の要素が含まれているため、処理できません")
    )
  str_ret	     
  )

;***********************************************************************;
;文字列の指定した位置から指定バイト数分の文字列を返す                   ;
;  (substr の2バイト文字対応バイト指定版)                             ;
;***********************************************************************;
(defun Jof_bsubstr2(      
		   str   ;文字列;
		   start ;指定した位置(バイト指定) 先頭は1バイト目;
		   byte  ;指定したバイト数;
		   / n lst_str nn lst_ret)
  (setq lst_str (Jof_str2clst str))
  (setq n 0)
  (while (and (<= (+ n (setq nn (Jof_chk_cbyte (car lst_str)))) (1- start)) (< start (strlen str)))
    (setq n (+ n nn))
    (setq lst_str (cdr lst_str))
    )
  (setq nn (- (strlen str) n))
  (setq n 0)
  (while (and (< n byte) (< n nn))
    (setq n (+ n (Jof_chk_cbyte (car lst_str))))
    (setq lst_ret (cons (car lst_str) lst_ret))
    (setq lst_str (cdr lst_str))
    )
  (Jof_clst2str (reverse lst_ret))  
  )