过滤ASCII码中的不可见字符, ASCII三部分, 各控制字符详解, 去^@,^M
今天產品部同事報告了一個BUG,經過調試發現,由于用戶輸入的字符串中,包含字符0x1E, 也就是”記錄分隔符”(Record Separator, Notepad++ 顯示為[RS]),導致JavaScript XML解析遭遇錯誤。于是就想在字符串中過濾掉這些沒多大用途的字符,同時又要保留部分常用的字符,例如換行,回車和水平制表符。于是寫了下面一個 PHP 函數:
/**
* 清理字符串中的部分不可見控制字符
*
* @param string $string 待處理字符串
* @return string 處理后的字符串
* @author fising(at)qq.com
*/
public static function clearInvisibleCharacter($string = '')
{
/* 排除 tab,
, 三個字符 */
$do_not_searches = array(chr(9), chr(10), chr(13));
/* 需清理的字符列表 */
$searches = array();
for ($i = 0; $i <= 31; $i++)
{
if (!in_array(chr($i), $do_not_searches))
{
$searches[] = chr($i);
}
}
$searches[] = chr(127);
return str_replace($searches, '', $string);
}
過濾ASCII碼中的不可見字符
上面所說的“不可見字符”,其實屬于ascii碼中的控制字符,它們是0到31、以及127,分別代表什么可查詢ascii碼表。
在展示頁面前,將文字中的控制字符改為空格(ascii32)
public static String filter(String content){
if (content != null && content.length() > 0) {
char[] contentCharArr = content.toCharArray();
for (int i = 0; i < contentCharArr.length; i++) {
if (contentCharArr[i] < 0x20 || contentCharArr[i] == 0x7F) {
contentCharArr[i] = 0x20;
}
}
return new String(contentCharArr);
}
return "";
}
其具體每個控制字符的含義,詳解介紹如下:
【ASCII中的Function/Control Code功能字符的詳細含義】
0 – NUL –NULl字符/空字符
ASCII字符集中的空字符,NULL,起初本意可以看作為NOP(中文意為空操作,就是啥都不做的意思),此位置可以忽略一個字符。
之所以有這個空字符,主要是用于計算機早期的記錄信息的紙帶,此處留個NUL字符,意思是先占這個位置,以待后用,比如你哪天想起來了,在這個位置在放一個別的啥字符之類的。
后來呢,NUL字符被用于C語言中,字符串的終結符,當一個字符串中間出現NUL / NULL,代碼里面表現為,的時候,就意味著這個是一個字符串的結尾了。這樣就方便按照自己需求去定義字符串,多長都行,當然只要你內存放得下,然后最后加一個,即空字符,意思是當前字符串到此結束。
1 – SOH –Start
OfHeading標題開始
如果信息溝通交流主要以命令和消息的形式的話,SOH就可以用于標記每個消息的開始。
1963年,最開始ASCII標準中,把此字符定義為Start of Message,后來又改為現在的Start Of Heading。
現在,這個SOH常見于主從(master-slave)模式的RS232的通信中,一個主設備,以SOH開頭,和從設備進行通信。這樣方便從設備在數據傳輸出現錯誤的時候,在下一次通信之前,去實現重新同步(resynchronize)。如果沒有一個清晰的類似于SOH這樣的標記,去標記每個命令的起始或開頭的話,那么重新同步,就很難實現了。
2 – STX –StartOfText文本開始
3 – ETX –End OfText文本結束
通過某種通訊協議去傳輸的一個數據(包),稱為一幀的話,常會包含一個幀頭,包含了尋址信息,即你是要發給誰,要發送到目的地是哪里,其后跟著真正要發送的數據內容。
而STX,就用于標記這個數據內容的開始。接下來是要傳輸的數據,最后是ETX,表明數據的結束。
其中,中間具體傳輸的數據內容,ASCII規范并沒有去定義,其和你所用的傳輸協議,具體自己要傳什么數據有關。
|
幀頭 |
數據或文本內容 |
|||
|
SOH(表明幀頭開始) |
。。。。(幀頭信息,比如包含了目的地址,表明你發送給誰等等) |
STX(表明數據開始) |
。。。(真正要傳輸的數據) |
ETX(表明數據結束) |
不過其中有趣的是,1963年,ASCII標準最初版本的時候,把現在的STX叫做EOA(End Of Address),ETX叫做(End Of Message)。這是因為,最早的時候,一個消息中,總是包含一個開始符和一個終止符。現在的新的定義,使得可以去發送一個固定長度的命令,而只用一個SOH表明幀頭開始即可,而不需要再加上一個命令終止符或幀頭結束符。
總結一下:
一般發送一個消息,包含了一個幀頭和后面真正要傳的數據。
而對于幀頭,屬于控制類的信息,這部分之前屬于命令,后面的真實要傳的數據屬于數據。即消息=幀頭+數據。
而之前的命令都要有個開始符和結束符,這樣就是:
消息
=幀頭
+要傳的數據
=幀頭開始+幀頭信息+幀頭結束
+要傳的數據
而現在新的定義,使得只需要:
消息
=幀頭+要傳的數據
= SOH(表明幀頭開始)+幀頭信息
+要傳的數據
= SOH(表明幀頭開始)+幀頭信息
+ STX +數據內容+ETX
就可以少用一個幀頭結束符。
而如今,在很多協議中,也常見到,一個固定長度的幀頭,后面緊接著就是數據了,而沒有所謂的幀頭結束符之類的東西去區分幀頭和數據。
4 – EOT –EndOfTransmission傳輸結束
5 – ENQ –ENQuiry請求
6 – ACK –ACKnowledgment回應/響應
7 – BEL – [audible]BELl
在ASCII字符集中,BEL,是個比較有意思的東東。因為其原先本意不是用來數據編碼的,于此相反,ASCII中的其他字符,都是用于字符編碼(即用什么字符,代表什么含義)或者起到控制設備的作用。BEL用一個可以聽得見的聲音,來吸引人們的注意,其原打算即用于計算機也用于一些設備,比如打印機等。C語言里面也支持此BEL,用a來實現這個響鈴。
8 – BS –BackSpace退格鍵
退格鍵的功能,隨著時間變化,意義也變得不同了。
起初,意思是,在打印機和電傳打字機上,往回移動一格光標,以起到強調該字符的作用。比如你想要打印一個a,然后加上退格鍵后,就成了aBS^。在機械類打字機上,此方法能夠起到實際的強調字符的作用,但是對于后來的CTR下時期來說,就無法起到對應效果了。
而現代所用的退格鍵,不僅僅表示光標往回移動了一格,同時也刪除了移動后該位置的字符。在C語言中,退格鍵可以用表示。
9 – HT –HorizontalTab水平制表符
ASCII中的HT控制符的作用是用于布局的。
其控制輸出設備前進到下一個表格去處理。而制表符Table/Tab的寬度也是靈活不固定的,只不過,多數設備上,制表符Tab的寬度都預定義為8。水平制表符HT不僅能減少數據輸入者的工作量,對于格式化好的文字來說,還能夠減少存儲空間,因為一個Tab鍵,就代替了8個空格,所以說省空間。
對于省空間的優點,我們現在來看,可能會覺得可笑,因為現在存儲空間已足夠大,一般來說根本不會需要去省那么點可憐的存儲空間,但是實際上在計算機剛發明的時候,存儲空間(主要指的是內存)極其有限也極其昂貴,而且像ZIP等壓縮方法也還沒發明呢,所以對于當時來說,對于存儲空間,那是能夠省一點是一點,省任何一點,都是好的,也都是不容易的,省空間就是省錢啊。
C語言中,用 表示制表符。
10 – LF –LineFeed換行
LF,直譯為(給打印機等)喂一行,意思就是所說的,換行。
換行字符,是ASCII字符集中,被誤用的字符中的其中一個。
LF的最原始的含義是,移動打印機的頭到下一行。而另外一個ASCII字符,CR(Carriage Return)才是將打印機的頭,移到最左邊即一行的開始,行首。很多串口協議和MS-DOS及Windows操作系統,也都是這么實現的。
而于此不同,對于C語言和Unix操作系統,其重新定義了LF字符的含義為新行,即LF和CR的組合才能表達出的,回車且換行的意思。
雖然你可以爭論哪種用法是錯的,但是,不可否認,是從程序的角度出發,C語言和Unix對此LF的含義實現顯得就很自然,而MS-DOS的實現更接近于LF的本意。
如果最開始ASCII標準中,及定義CF也定義newline,那樣意思會清楚,會更好理理解:
LF表示物理上的,設備控制方面的移動到下一行(并沒有移動到行首);
新行(newline)表示邏輯上文本分隔符,即回車換行。
不過呢,現在人們常將LF用做newline新行的功能,而大多數文本編輯軟件也都可以處理單個LF或者CR/LF的組合了。
LF在C語言中,用
表示。
11 – VT –VerticalTab垂直制表符
垂直制表符,類似于水平制表符Tab,目的是為了減少布局中的工作,同時也減少了格式化字符時所需要存儲字符的空間。VT控制碼用于跳到下一個標記行。說實話,還真沒看到有些地方需要用這個VT呢,因為一般在換行的時候,都是用LF代替VT了。
12 – FF –FormFeed
換頁
設計換頁鍵,是用來控制打印機行為的。當打印機收到此鍵碼的時候,打印機移動到下一頁。不同的設備的終端對此控制碼所表現的行為各不同。有些會去清除屏幕,而其他有的只是顯示^L字符或者是只是新換一行而已。Shell腳本程序Bash和Tcsh的實現方式是,把FF看作是一個清除屏幕的命令。C語言程序中用f表示FF(換頁)。
13 – CR – Carriage return機器的滑動部分/底座
返回->回車
CR回車的原意是讓打印頭回到左邊界,并沒有移動到下一行。
隨著時間流逝,后來人把CR的意思弄成了Enter鍵,用于示意輸入完畢。在數據以屏幕顯示的情況下,人們在Enter的同時,也希望把光標移動到下一行。因此C語言和Unix操作系統,重新定義了LF的意思,使其表示為移動到下一行。當輸入CR去存儲數據的時候,軟件也常常隱式地將其轉換為LF。
14 – SO –ShiftOut不用切換
15 – SI –ShiftIn
啟用切換
早在1960s年代,定義ASCII字符集的人,就已經懂得了,設計字符集不單單可以用于英文字符集,也要能應用于外文字符集,是很重要的。
定義Shift In和Shift Out的含義,即考慮到了此點。
最開始,其意為在西里爾語和拉丁語之間切換。西里爾ASCII定義中,KOI-7用到了Shift字符。拉丁語用Shift去改變打印機的字體。在此種用途中,SO用于產生雙倍寬度的字符,而用SI打印壓縮的字體。
16 – DLE –DataLinkEscape數據鏈路轉義
有時候,我們需要在正在進行的通信過程中去發送一些控制字符。但是,總有一些情況下,這些控制字符卻被看成了普通的數據流,而沒有起到對應的控制效果。而ASCII標準中,定義DLE來解決這類問題。
如果數據流中檢測到了DLE,數據接收端則對其后面接下來的數據流中的字符,另作處理。而關于具體如何處理這些字符,ASCII規范中則沒有具體定義,而只是弄了個DLE去打斷正常數據的處理,告訴接下來的數據,要特殊對待。根據Modem中的Hayes通信協議DLE定義為“無聲+++無聲”。以我的觀點,這樣可能會更好:如果Hayes協議沒有把DLE處理為嵌入通訊的無聲狀態,那樣就符合現存的標準了。然而Hayes的開發者卻覺得+++用的頻率要遠高于原始的DLE,所以才這么定義了。
17 – DC1 –DeviceControl 1 / XON – Transmission on
這個ASCII控制字符盡管原先定義為DC1,
但是現在常表示為XON,用于串行通信中的軟件流控制。其主要作用為,在通信被控制碼XOFF中斷之后,重新開始信息傳輸。用過串行終端的人應該還記得,當有時候數據出錯了,按Ctrl+Q(等價于XON)有時候可以起到重新傳輸的效果。這是因為,此Ctrl+Q鍵盤序列實際上就是產生XON控制碼,其可以將那些由于終端或者主機方面,由于偶爾出現的錯誤的XOFF控制碼而中斷的通信解鎖,使其正常通信。
18 – DC2 –DeviceControl 2
19 – DC3 –DeviceControl 3 / XOFF – Transmission off傳輸中斷
20 – DC4 –DeviceControl 4
21 – NAK –NegativeAcKnowledgment負面響應->無響應,非正常響應
22 – SYN –SYNchronous idle
23 – ETB –End ofTransmissionBlock塊傳輸中止
24 – CAN –CANcel取消
25 – EM –End ofMedium
已到介質末端,介質存儲已滿
EM用于,當數據存儲到達串行存儲介質末尾的時候,就像磁帶或磁頭滾動到介質末尾一樣。其用于表述數據的邏輯終點,即不必非要是物理上的達到數據載體的末尾。
26 – SUB –SUBstitute character替補/替換
27 – ESC –ESCape逃離/取消
字符Escape,是ASCII標準的首創的,由Bob Bemer提議的。用于開始一段控制碼的擴展字符。如此,即可以不必將所有可能想得到的字符都放到ASCII標準中了。因為,新的技術可能需要新的控制命令,而ESC可以用作這些字符命令的起始標志。ESC廣泛用于打印機和終端,去控制設備設置,比如字體,字符位置和顏色等等。如果最開始的ASCII標準中,沒有定義ESC,估計ASCII標準早就被其他標準所替代了,因為其沒有包含這些新出現的字符,所以肯定會有其他新的標準出現,用于表示這些字符的。即,ESC給開發者提供了,可以根據需要而定義新含義的字符的可能。
28 – FS –FileSeparator文件分隔符
文件分隔符是個很有意思的控制字符,因為其可以讓我們看到1960s年代的時候,計算機技術是如何組織的。我們現在,習慣于隨即訪問一些存儲介質,比如RAM,磁盤,但是在定義ASCII標準的那個年代,大部分數據還是順序的,串行的,而不是隨機訪問的。此處所說的串行的,不僅僅指的是串行通信,還指的是順序存儲介質,比如穿孔卡片,紙帶,磁帶等。在串行通信的時代,設計這么一個用于表示文件分隔符的控制字符,用于分割兩個單獨的文件,是一件很明智的事情。而FS的原因就在于此。
29 – GS –GroupSeparator分組符
ASCII定義控制字符的原因中,其中一條就是考慮到了數據存儲方面的情況。大部分情況下,數據庫的建立,都和表有關,包含了對應的記錄。同一個表中的所有的記錄,屬于同一類型。不同的表中的記錄,屬于對應的不同的類型。而分組符GS就是用來分隔串行數據存儲系統中的不同的組。值得注意的是,當時還沒有使用word的表格,當時ASCII時代的人,把他叫做組。
30 – RS –RecordSeparator記錄分隔符
記錄分隔符RS用于分隔在一個組或表內的多個記錄。
31 – US –UnitSeparator單元分隔符
在ASCII定義中,在數據庫中所存儲的,最小的數據項,叫做Unit單元。而現在我們稱其field域。單元分隔符US用于分割串行數據存儲環境下的不同的域。
現在大部分的數據庫實現,要求大部分類型都擁有固定的長度。
盡管大部分時候可能用不到,但是對于每一個域,卻都要分配足夠大的空間,用于存放最大可能的成員變量。這樣的做法,占用了大量的存儲空間,而US控制碼允許域具有可變的長度。在1960s年代,數據存儲空間很有限,用US這個單元分隔符,將不同單元分隔開,這樣就可以實現更高效地存儲那些寶貴的數據。另一方面,串行存儲的存儲效率,遠低于RAM和磁盤中所實現的表格存儲。我個人無法想象,如果現在的數據,還是存儲在自帶或者帶滾輪的磁帶上,會是何種景象。
32 – SP – WhiteSPace空格鍵
也許你會爭論說,空格鍵是否真的能算是一個控制字符?因為現在在普通文字中使用空格鍵是如此常見。
但是,既然水平制表符和退格鍵在ASCII中,都被叫做控制字符了,那么我覺得也很自然地,可以把空格鍵(向前的空格)也叫做控制字符,畢竟,其本身并不代表一個真正的可見的字符,而僅僅只是很常用于輸出設備,用于處理位置前向移動一格,清除當前位置的內容而已。在很多程序中,比如字符處理程序,白空格同樣可能從導致行尾轉到下一行行首,而網絡瀏覽器將多個空格組合成單個空格輸出。
所以,這更加堅定了我的想法,覺得完全可以把空格看成是一個控制字符,而不僅僅是一個很獨特的普通字符。
127 – DEL –DELete
刪除
有人也許會問,為何ASCII字符集中的控制字符的值都是很小的,即0-32,而DEL控制字符的值卻很大,是127。這是由于這個特殊的字符是為紙帶而定義的。而在那個時候,絕大多數的紙帶,都是用7個孔洞去編碼數據的。而127這個值所對應的二進制值為111 1111b,表示所有7個比特位都是高,所以,將DEL用在現存的紙帶上時,所有的洞就都被穿孔了,就把已經存在的數據都擦出掉了,就起到了對應的刪除的作用了。
【各種字符的標準的讀法/叫法】
常見ASCII字符,以及其他非常見的字符,Unicode中的字符,其他特殊字符等等,這些字符的英文叫法,可以去Unicode官方找到:
http://www.unicode.org/charts/
比如:
ASCII字符/字母的叫法/讀法
如何讀
:
1. C0 Control and Basic Latin Range:0000-007F
http://www.unicode.org/charts/PDF/U0000.pdf
2.Alphabetic Presentation Forms Range:FB00-FB4F
http://www.unicode.org/charts/PDF/UFB00.pdf
3.CJK Compatibility Forms
http://www.unicode.org/charts/PDF/UFE30.pdf
4.Fullwidth ASCII Punctuation
http://www.unicode.org/charts/PDF/UFF00.pdf
【引用】
1.C0 and C1 control codes
http://en.wikipedia.org/wiki/C0_and_C1_control_codes
2. Control Character
http://en.wikipedia.org/wiki/Control_character
3.
ASCII character map
http://www.lammertbies.nl/comm/info/ascii-characters.html
4.
百度百科:ASCII
http://baike.baidu.com/view/15482.htm
5.ASCII編碼表
http://www.dreamdu.com/xhtml/ascii/
ASCII碼大致可以分作三部分組成。
第一部分:ASCII非打印控制字符表
ASCII表上的數字0–31分配給了控制字符,用于控制像打印機等一些外圍設備。例如,12代表換頁/新頁功能。此命令指示打印機跳到下一頁的開頭。(參詳ASCII碼表中0-31)
第二部分:ASCII打印字符
數字 32–126 分配給了能在鍵盤上找到的字符,當您查看或打印文檔時就會出現。數字127代表 DELETE 命令。(參詳ASCII碼表中32-127)
ASCII碼表 0-127
第三部分:擴展ASCII打印字符
擴展的ASCII字符滿足了對更多字符的需求。擴展的ASCII包含ASCII中已有的128個字符(數字0–32顯示在下圖中),又增加了128個字符,總共是256個。即使有了這些更多的字符,許多語言還是包含無法壓縮到256個字符中的符號。因此,出現了一些ASCII的變體來囊括地區性字符和符號。例如,許多軟件程序把ASCII表(又稱作ISO8859-1)用于北美、西歐、澳大利亞和非洲的語言。
Vim里常見的幾個不可見字符:
^@ = 0x00 Null值
^H = 0x08 退格
^I = 0x09 水平制表
^J = 0x0A 換行
^M = 0x0D 回車
去掉^M回車:
:%s/^M//g # vi中將^M替換成回車。
$ sed -e 's/^M//g' myfile.txt #直接操作文件
注意:這里的“^M”要使用“CTRL-V CTRL-M”生成,而不是直接鍵入“^M”。
去掉^@:
sed -r 's/x0//g' file>file1
cat -v file打印不可見字符。
【什么是Function Code功能碼或Function Character功能字符】
ASCII字符集,大家都知道吧,最基本的包含了128個字符。其中前32個,0-31,即0x00-0x1F,都是不可見字符。這些字符,就叫做控制字符。
這些字符沒法打印出來,但是每個字符,都對應著一個特殊的控制功能的字符,簡稱功能字符或功能碼Function Code。
簡言之:ASCII中前32個字符,統稱為Function Code功能字符。
此外,由于ASCII中的127對應的是Delete,也是不可見的,所以,此處根據筆者的理解,也可以歸為Function Code。
此類字符,對應不同的“功能”,起到一定的“控制作用”,所以,稱為控制字符。
關于每個控制字符的控制功能縮寫,參見下表:
表格1
ASCII中的控制字符
|
十 進制 |
十六 進制 |
控制 字符 |
轉義 字符* |
說明 |
Ctrl + 下列字母* |
|
0 |
00 |
NUL |
Null character(空字符) |
@ (Shift + 2) |
|
|
1 |
01 |
SOH |
Start of Header(標題開始) |
^A |
|
|
2 |
02 |
STX |
Start of Text(正文開始) |
^B |
|
|
3 |
03 |
ETX |
End of Text(正文結束) |
^C |
|
|
4 |
04 |
EOT |
End of Transmission(傳輸結束) |
^D |
|
|
5 |
05 |
ENQ |
Enquiry(請求) |
^E |
|
|
6 |
06 |
ACK |
Acknowledgment(收到通知/響應) |
^F |
|
|
7 |
07 |
BEL |
a |
Bell(響鈴) |
^G |
|
8 |
08 |
BS |
Backspace(退格) |
^H |
|
|
9 |
09 |
HT |
|
Horizontal Tab(水平制表符) |
^I |
|
10 |
0A |
LF |
|
Line feed(換行鍵) |
^J |
|
11 |
0B |
VT |
v |
Vertical Tab(垂直制表符) |
^K |
|
12 |
0C |
FF |
f |
Form feed(換頁鍵) |
^L |
|
13 |
0D |
CR |
Carriage return(回車鍵) |
^M |
|
|
14 |
0E |
SO |
Shift Out(不用切換) |
^N |
|
|
15 |
0F |
SI |
Shift In(啟用切換) |
^O |
|
|
16 |
10 |
DLE |
Data Link Escape(數據鏈路轉義) |
^P |
|
|
17 |
11 |
DC1 |
Device Control 1(設備控制1) /XON(Transmit On) |
^Q |
|
|
18 |
12 |
DC2 |
Device Control 2(設備控制2) |
^R |
|
|
19 |
13 |
DC3 |
Device Control 3(設備控制3) /XOFF(Transmit Off) |
^S |
|
|
20 |
14 |
DC4 |
Device Control 4(設備控制4) |
^T |
|
|
21 |
15 |
NAK |
Negative Acknowledgement(拒絕接收/無響應) |
^U |
|
|
22 |
16 |
SYN |
Synchronous Idle(同步空閑) |
^V |
|
|
23 |
17 |
ETB |
End of Trans the Block(傳輸塊結束) |
^W |
|
|
24 |
18 |
CAN |
Cancel(取消) |
^X |
|
|
25 |
19 |
EM |
End of Medium(已到介質末端/介質存儲已滿) |
^Y |
|
|
26 |
1A |
SUB |
Substitute(替補/替換) |
^Z |
|
|
27 |
1B |
ESC |
e |
Escape(溢出/逃離/取消) |
[ |
|
28 |
1C |
FS |
File Separator(文件分割符) |
||
|
29 |
1D |
GS |
Group Separator(分組符) |
] |
|
|
30 |
1E |
RS |
Record Separator(記錄分隔符) |
^ (Shit + 6) |
|
|
31 |
1F |
US |
Unit Separator(單元分隔符) |
_ (Shift + -) |
|
|
32 |
20 |
SP |
White space |
[Space] * |
|
|
127 |
7F |
DEL |
Delete(刪除) |
?* |
注(*):
1.轉義字符:即在C語言中或其他地方如何表示。
2.用鍵盤輸入控制字符:其中,32是空格鍵,都不需要加Ctrl鍵,即可直接輸入。
3.127是Delete鍵,除了可以用鍵盤上的刪除鍵輸入,也可以用Ctrl+?輸入。
4.可以通過“Ctrl+對應按鍵”實現上述控制字符的輸入,你可能遇到的一些,比如:用Ctrl+V輸入SYNC,Ctrl+M輸入Enter(當然也可以直接用Enter鍵,但是在Windows下面,其可能會發送兩個字符:CR和LF),Ctrl+Q輸入XON,Ctrl+S輸入XOFF等等。
^
總結
以上是生活随笔為你收集整理的过滤ASCII码中的不可见字符, ASCII三部分, 各控制字符详解, 去^@,^M的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 海军军医大学生长军官学员学号按什么排的
- 下一篇: Sagemath在ctf密码学中的使用