newLISP你也行 --- 字符串.

2012-05-25 20:34

newLISP你也行 --- 字符串.

by address-withheld@my.opera.com.invalid (F0)

at 2012-05-25 12:34:04

original http://my.opera.com/freewinger/blog/show.dml/49009172

  #############################################################################
  # Name:         newLISP你也行 ---
  # Author:       黄登(winger)
  # Project:      http://code.google.com/p/newlisp-you-can-do
  # Gtalk:        free.winger@gmail.com
  # Gtalk-Group:  zen0code@appspot.com
  # Blog:         http://my.opera.com/freewinger/blog/
  # QQ-Group:     31138659
  # 大道至简 -- newLISP
  #
  # Copyright 2012 黄登(winger) All rights reserved.
  # Permission is granted to copy, distribute and/or
  # modify this document under the terms of the GNU Free Documentation License,
  # Version 1.2 or any later version published by the Free Software Foundation;
  # with no Invariant Sections, no Front-Cover Texts,and no Back-Cover Texts.
  #############################################################################
 
 
 
 
          自由固不是钱所买到的,但能够为钱而卖掉。        --- 鲁迅
 
      现实中, 在人和计算机交互中, 涉及到最多的就是字符串了.
      以至于大部分的数据输入都被当做字符串来处理.
      如果说列表是天地, 那字符串就一定是这天地间的横流.
 
 
  .  newLISP中的字符串
      Strings in newLISP code
 
      newLISP 处理字符串的能力无疑是强大的, 各种方便的刀具都给你备齐了, 每一把都
  是居家宅男, 杀码越货, 的必备神器.
 
      广告完毕, 言归正传.~_~~
 
      在nl里有三种方法可以表示字符串:
 
      用双引号围起来 ;优点按键更少, 而且转义字符有效, 比如"\n"
      (set 's "this is a string")
 
      用花括号围起来 ;优点过滤一切转义字符
      (set 's {this is a string})
 
      用专门的标识码围起来 ;除了上面的优点外,他还可以构造大于2048字节的字符串
      (set 's [text]this is a string[/text])
 
 
      第一和第二中方法构建的字符串不能超过 2048 个字节.
      很多人会觉得既然有了第二种, 为什么还要有第一种?
      让我们测试下下面的代码
 
  > {{}
 
  ERR: string token too long : "\{}"
 
  > """
  """
 
      看到没, 花括号的好处就是过滤一切的转义字符, 转义字符到了里面没有任何作用.
  如果你要print 一个字符串:
 
 
  > (print {\n road to freedom})
  \n road to freedom"\n road to freedom"
  > (print "\n road to freedom")
 
   road to freedom"\n road to freedom"
 
      花括号内内的转义字符没效了, 根本没换行. 这三种方法就第一种方法, 可以在内部
  使用自己的TAG 双引号.
 
      第二种方法, 花括号, 这种方法我是非常鼓励使用的, 为什么, 方便啊, 不用在转义
  字符前加个反斜杠了, 在构造正则表达式的时候尤其好用.
 
  > (println "\t45")
          45
  "\t45"
  > (println "\t45")
  \t45
  "\t45"
  > (println {\t45})
  \t45
  "\t45"
 
  > (regex "\d" "a9b6c4")
  ("9" 1 1)
 
  > (regex {\d} "a9b6c4")
  ("9" 1 1)
 
      字符串通常支持以下几种转义字符:
 
  character   description
  \"          for a double quote inside a quoted string
  \n          for a line-feed character (ASCII 10)
  \r          for a return character (ASCII 13)
  \t          for a TAB character (ASCII 9)
  \nnn        for a three-digit ASCII number (nnn format between 000 and 255)
  \xnn        for a two-digit-hex ASCII number (xnn format between x00 and xff)
 
  (set 's "this is a string \n with two lines")
  (println s)
 
  this is a string
  with two lines
 
  (println "\110\101\119\076\073\083\080") ; 十进制 ASCII
  newLISP
 
  (println "\x6e\x65\x77\x4c\x49\x53\x50") ; 十六进制 ASCII
  newLISP
 
 
      如果要你反过来把字符串写成上面的各种数字字符串, 该怎么呢?
      提示:formatunpack .
 
 
      第三种[text] [\text] 通常用来处理超长的字符串数据(大于 2048 字节), 比如web
  页面. nL 在传递长字符串的时候, 也会自动使用这种格式.
 
 
  (set 'novel (read-file {my-latest-novel.txt}))
  ;->
  [text]
  It was a dark and "stormy" night...
  ...
  The End.
  [/text]
 
      使用 length 可以得到字符串的长度:
 
  (length novel)
  ;-> 575196
 
      newLISP 可以高效的处理数百万的字符串.
      如果要统计unicode 字符串的长度, 必须使用utf8 版本的 newLISP:
 
  (utf8len (char 955))
  ;-> 1
  (length (char 955))
  ;-> 2
  > (utf8len "个")
  4
  > (length "个")
  2
 
      cmd.exe 在处理非ascii 字符的时候会产生很多问题, 几乎无法解决, 但是非Win32
  console 没这个问题.
 
 
  . 构造字符串
      Making strings
 
 
      有N种方法构造字符串. 到处都是字符串. 遍地都是字符串...
      如果想一个一个字符的构造的话可以用 char :
 
  (char 33)
  ;-> "!"
 
  > (char "a")
  97
 
  > (char 0x61)
  "a"
 
  > (char 97)
  "a"
 
 
      char 只能处理一个字符, 他可以将字符转换成数字, 也可以将数字转换成字符.
 
 
  (join (map char (sequence (char "a") (char "z"))))
  ;-> "abcdefghijklmnopqrstuvwxyz"
 
 
      char 获得 "a""z" ascii码, 然后用sequence 产生一个数字序列, 接着用map
  映射 char 函数到每个数字, 产生数字相对应的字符. 最后join 将整个列表合成一个字
  符串.
 
      我们也可以给 join 传递一个参数, 做分隔符.
 
 
  (join (map char (sequence (char "a") (char "z"))) "-")
  ;-> "a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z"
 
 
      和 join 类似 append 也可以连接字符串. (大部分的列表函数也可用于字符串)
 
 
  (append "con" "cat" "e" "nation")
  ;-> "concatenation"
 
 
      构造列表的时候我们用list , 构造字符串我们用string .
      string 可以将各种参数组合成, 一个字符串.
 
 
  (define x 42)
  (string {the value of } 'x { is } x)
  ;-> "the value of x is 42"
 
 
      更精细的字符串输出可以使用format , 稍后就会见到.
      dup 可以复制字符串:
 
 
  > (dup "帅锅" 5)
  "帅锅帅锅帅锅帅锅帅锅"
 
 
      date 会产生一个包含当前时间信息的字符串.
 
 
  > (date)
  "Mon May 14 15:50:34 2012"
 
 
  > (date 1234567890)
  "Sat Feb 14 07:31:30 2009"
 
 
 
  .  字符串手术
      String surgery
 
 
      这里不知道怎么翻译鸟, 手术啊. 听起来很恐怖. 其实就是永久性改变.
 
-     很多函数都可以操作字符串, 部分是具有破坏性的(destructive 这些函数在手册
  , 都有一个 ! 标志).
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (reverse t)
  ;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
  t
  ;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
 
 
      之前已经说过要用这些函数又不想破坏原来的数据, 就要用 copy.
 
 
  (reverse (copy t))
  ;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
  t
  ;-> "a hypothetical one-dimensional subatomic particle"
 
 
 
      上面的reverse 永久性的改变了 t. 但是下面的大小写转换函数, 却不会改变原字符
  .
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (upper-case t)
  ;-> "A HYPOTHETICAL ONE-DIMENSIONAL SUBATOMIC PARTICLE"
  (lower-case t)
  ;-> "a hypothetical one-dimensional subatomic particle"
  (title-case t)
  ;-> "A hypothetical one-dimensional subatomic particle"
  t
  ;-> "a hypothetical one-dimensional subatomic particle"
 
 
 
  . 子串
      Substrings
 
      如果需要抽取字符串中的一部分可以用以下的方法:
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (first t)
  ;-> "a"
  (rest t)
  ;-> " hypothetical one-dimensional subatomic particle"
  (last t)
  ;-> "e"
  (t 2)
  ;-> "h"
 
 
      你会发现这和上一章介绍的列表操作好像.nL里头大部分的列表操作函数, 也同样
  可以操作字符串. 其中就包括各种选取函数.
 
 
  1:  字符串分片
      String slices
 
 
      slice 可以将从一个现存的字符串中, 分割出一个新的字符串.
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (slice t 15 13) ;从第15个位置开始, 提取出出13个字符
  ;-> "one-dimension"
  (slice t -8 8)  ;从倒数第8个位置开始, 提取出8个字符
  ;-> "particle"
  (slice t 2 -9)  ;从第2个位置开始, 提取到倒数第9个字符为止(第9个字符不算)
  ;-> "hypothetical one-dimensional subatomic"
  (slice "schwarzwalderkirschtorte" 19 -1) ;同上, 最后一个字符不取
  ;-> "tort"
 
 
      当然, 字符串也可以用隐式操作.
 
 
  (15 13 t)
  ;-> "one-dimension"
  (0 14 t)
  ;-> "a hypothetical"
 
 
      上面提取的字符串都是连续的. 如果要抽取出分散的字符. 就得用 select :
 
 
  (set 't "a hypothetical one-dimensional subatomic particle")
  (select t 3 5 24 48 21 10 44 8)
  ;-> "yosemite"
  (select t (sequence 1 49 12)) ; 从第1个字符开始, 每隔12个提取出一个字符
  ;-> " lime"
 
  > (help select)
  syntax: (select <string> <list-selection>)
  syntax: (select <string> [<int-index_i> ... ])
 
 
       <list-selection> 列表中包含了要提取的字符的位置.
 
 
 
  2:  改变字符串的首位
      Changing the ends of strings
 
 
      choptrim 可以给字符串做收尾切除术, 他们都具破坏性.
      切切切...
 
      chop 只能切除一个指定位置的字符...
 
 
  (chop t) ; 默认是最后一个字符
  ;-> "a hypothetical one-dimensional subatomic particl"
  (chop t 9) ; 切除第9个字符
  ;-> "a hypothetical one-dimensional subatomic"
 
 
      trim 修剪掉存在于字符串头尾的指定字符.
 
  (set 's " centred ")
  (trim s) ; defaults to removing spaces
  ;-> "centred"
 
  (set 's "------centred------")
  (trim s "-")
  ;-> "centred"
 
  (set 's "------centred*******")
  (trim s "-" "
"
) ;可以分别指定需要修剪的头 和 尾 "字符"
  ;-> "centred"
 
 
  3:  pushpop 字符串
      push and pop work on strings too
 
 
      push 可以将元素压入指定字符串的指定位置. pop  相反.
      如果没有指定位置, 默认为字符串的第一个位置.
 
 
  (set 't "some ")
  (push "this is " t)
  (push "text " t -1)
  ;-> t is now "this is some text"
 
 
      pushpop 都返回压入或者弹出的元素, 而不是目标字符串. 这样操作大的字符串
  , 就会更快. 否则你就得用slice 屏蔽输出了.
 
  >(help pop)
  syntax: (pop <str> [<int-index> [<int-length>]])
 
      可以指定pop字符的数量, [<int-length>] .
 
 
  (set 'version-string (string (sys-info -2)))
  ; eg: version-string is "10402"
  (set 'dev-version (pop version-string -2 2)) ; 总是两个数字
  ; version-string is now "02"
  (set 'point-version (pop version-string -1)) ; 总是一个数字
  ; version-string is now "4"
  (set 'version version-string) ; 一位或者两位 99?
  (println version "." point-version "." dev-version " on " ostype)
  10.4.02 on Win32
  "Win32"
 
 
      ostype 返回操作系统类型.
 
 
  . 修改字符串
      Modifying strings
 
      有两种方法修改字符串, 一种, 指定具体的位置. 第二种指定特定的内容.
 
  1:  通过索引修改字符串
      Using index numbers in strings
 
 
      好久以前是有nth-setset-nth, 不过鉴于各种 set 和 被 set , 其操作方法
  和返回值的复杂性. 在现今的版本中, 他们都已经消失不见了. 不过我们可以使用隐式索
  , 操作访问指定位置的元素.
 
  > (set 'str "thinking newLISP !")
  "thinking newLISP !"
  > (setf (str 0) "I t")
  "I T"
  > str
  "I Thinking newLISP !"
 
 
  2:  改变字符串的子串
      Changing substrings
 
 
      很多时候, 你无法确切的知道, 需要操作的字符的索引, 或者找出来的代价太大.\
      这时候就可以用replace 替换所有符合自己要求的字符串部分...
 
 
  > (help replace)
  syntax: (replace <str-key> <str-data> <exp-replacement>)
  syntax: (replace <str-pattern> <str-data> <exp-replacement> <int-regex-option>)
 
 
  (replace old-string source-string replacement)
  So:
  (set't "a hypothetical one-dimensional subatomic particle")
  (replace "hypoth" t "theor") ;将字符串中所有的hypoth替换成theor
  ;-> "a theoretical one-dimensional subatomic particle"
 
 
  replace 是破坏性函数, 如果你不想改变原来的字符串, 可以使用copy 或者 string :
 
 
  (set't "a hypothetical one-dimensional subatomic particle")
  (replace "hypoth" (string t) "theor")
  ;-> "a theoretical one-dimensional subatomic particle"
  t
  ;-> "a hypothetical one-dimensional subatomic particle"