2018年3月29日 星期四

研究 | 大端 (Big Endian) 與 小端 (Little Endian) 的差異

什麼是記憶體

從軟體的角度,記憶體可以想像成一塊連續的儲存空間,最小的儲存單位是位元組(Byte)。
雖然可以操縱位元(bit),但還是以位元組(Byte)為最小單位做儲存。

例如:

用雞蛋盒對照記憶體,雞蛋盒就是儲存空間,一個雞蛋就是最小的儲存單位。

如何指定存取哪些記憶體位元組

記憶體可以儲存相當多的位元組(Byte),為了指定要存取哪一個,每一個位元組(Byte)都有一個號碼做代表,就是記憶體位址(Memory Address),因為是連續的儲存空間,所以記憶體位址(Memory Address)也是連續的。

例如:

以 4GB 記憶體來說 (GB 的 G 是 2 的 30 次方,而 B 是 Byte)
可以儲存 2 的 32 次方個位元組(Bytes) = 4,294,967,296 個位元組(Byte)
記憶體位址從 0 開始,所以範圍就是從 0 到 4,294,967,295

一次可以存取多少位元組

雖然記憶體位址是以位元組(Byte)來編號,但存取時可以"一次"存取(從硬體角度)多個位元組(Byte)。

例如:

以 C 語言來說(假設編譯成64位元程式)
"一次"可以存取如下 (運行在64位元CPU,記憶體寬度為64位元)

char  = 1個位元組(1-Byte) = 8位元(8-bit)
short = 2個位元組(2-Byte) = 16位元(16-bit)
int   = 4個位元組(4-Byte) = 32位元(32-bit)
long  = 8個位元組(8-Byte) = 64位元(64-bit)

資料如何寫入記憶體?資料從記憶體被讀取到哪裡

不考慮 DMA 與 cache,當程式要做運算時,其實是 CPU 讀取記憶體的資料後放到 CPU 的暫存器(register)做運算,而運算結果也是從 CPU 的暫存器(register)寫回到記憶體。因此提到存取記憶體,就要想到暫存器(register)。

什麼是位元組順序 Endianness什麼是 Big endian什麼是 Little endian

指的是多位元組(Multibyte)資料儲存在記憶體的順序

因為"一次"對記憶體寫入或讀取一個位元組(Byte)沒有順序問題

但是"一次"對記憶體寫入或讀取多個位元組(Multibyte)會有順序問題

而 Big endian 與 Little endian 就是兩種不同的儲存順序

底下圖片說明,從記憶體寫入或讀取4-Byte時,Big endian 與 Little endian 的差異

說明:
1. 圖中 Memory 下的 A, A+1, A+2, A+3 指的是記憶體位址,A 是低記憶體位址,  
   A+3是高記憶體位址。
2. 圖中 Register 上的 24, 16, 8, 0 指的是第幾位元(bit),0 是 LSB。






2018年3月20日 星期二

研究 | LFSR (Linear Feedback Shift Registers)

LFSR (Linear Feedback Shift Registers)

分兩種
1. Fibonacci (又稱 Many-to-one 或 External feedback)
2. Galois (又稱 One-to-many 或 Internal feedback)









圖片連結自 
https://www.ctimes.com.tw/DispArt/tw/16123011119R.shtml

設計良好的 LFSR 產生出來的 sequence 可以涵蓋所有可能的值減 1 種的 sequence,也就是maximal-length,例如:4-bit LFSR 可以產生 (2^4-1) = 15 種 sequence。而設計不良可能會發生 loop 導致產生的 sequence 的值只會在一個範圍內。

參考
https://www.eetimes.com/document.asp?doc_id=1274550
https://www.eetimes.com/document.asp?doc_id=1274551
https://www.eetimes.com/document.asp?doc_id=1274552
https://en.wikipedia.org/wiki/Linear-feedback_shift_register
http://www.ti.com/lit/an/scta036a/scta036a.pdf
http://www.eng.auburn.edu/~strouce/class/elec6250/LFSRs.pdf
https://www.ctimes.com.tw/DispArt/tw/16123011119R.shtml

應用
1. 加密解密 (Encryption and Decryption)
    a. 串流加密法 (Stream Ciphers)
        http://security.nknu.edu.tw/textbook1ed/CHAP2-Cipher.pdf
    b. Scrambling
        http://epaper.gotop.com.tw/pdf/ach014500.pdf
2. CRC (Cyclic redundancy check)
    a. http://www.ciphersbyritter.com/ARTS/CRCMYST.HTM
    b. http://blog.sina.com.cn/s/blog_62d9edac01015lsd.html
3. 資料壓縮 (Data compression)
4. BIST (Built-in self-test)
5. 隨機亂數序列 PRBS (Pseudo Random Bit Sequence)
6. 假亂數 (Pseudo-random number)






2018年3月19日 星期一

研究 | Switch 的三種 switching 方式

Switch 的三種 switching 方式

1. Store and forward
儲存整個 frame 到 switch 的記憶體,檢查 CRC 後,沒有錯誤則根據 frame 的 destination MAC address (DMAC) 查詢 forwarding table 後轉發出去,如果有錯誤則丟棄 frame。

優點: 可以完整偵測各種錯誤的 frame

缺點: 延遲時間長 (long latency)

2. Cut-through
只儲存 frame 的 destination MAC address (DMAC) 到 switch 的記憶體,查詢 forwarding table 後就轉發出去。

優點: 延遲時間短 (low latency)

缺點: 無法偵測錯誤的 frame,所以連錯誤的 frame 也轉發,由接收端的網卡來檢查 frame 的正確性

3. Fragment-free (Cut-through 的改良)
儲存 frame 的前 64-byte 到 switch 的記憶體,因為大部分的網路錯誤或是 collision 都是發生在前 64-byte。雖然延遲時間比 Cut-through 久一點,但是可以處理一些錯誤的 frame (例:runt frame)。

參考
1. http://www.ciscopress.com/articles/article.asp?p=357103&seqNum=4
2. http://felixhis2011.pixnet.net/blog/post/41439040-switch
3. https://hlchang.com/2010/08/cut-through-%E6%8D%B2%E5%9C%9F%E9%87%8D%E4%BE%86/
4. http://www.omnisecu.com/cisco-certified-network-associate-ccna/methods-of-switching.php
5. http://www.netadmin.com.tw/article_content.aspx?sn=1203290001&jump=1
6. https://www.cisco.com/c/en/us/products/collateral/switches/nexus-5020-switch/white_paper_c11-465436.html
7. https://community.fs.com/blog/comparison-between-store-and-forward-switching-and-cut-through-switching.html


2018年3月9日 星期五

『C語言』使用 json-c存取JSON格式 安裝教學/程式範例/完整說明

JSON 是一個輕量的資料交換格式,如果要用 C 語言存取 json,有許多 open source library 可以選擇,此處以 json-c 為例,提供讀取 json 與產生 json 的方法。

此範例會先產生如下的 json,並讀取此 json 做顯示。

{
   "NULL":null,
   "BOOLEAN":true,
   "STRING":"string",
   "NUMBER":100,
   "DOUBLE":3.141593,
   "ARRAY":[ "str1", "str2", "str3" ],
   "OBJECT":{  
      "MEMBER_NULL":null,
      "MEMBER_BOOLEAN":false,
      "MEMBER_STRING":"string",
      "MEMBER_NUMBER":200,
      "MEMBER_DOUBLE":0.123457,
      "MEMBER_ARRAY":[ 1, 2, 3 ]
   }
}

測試步驟 (此測試以 ubuntu 16.04.1 為測試環境)
1. 安裝 json-c library
    apt install apt-get install libjson-c-dev
2. 確認安裝 json-c library
    apt show libjson-c-dev
3. 將底下範例存成 example_json.c
4. 編譯程式
    gcc -o example_json example_json.c -ljson-c
5. 執行程式
    ./example_json
6. 檢查產生的 json
    cat example.json
7. 可以將 example.json 的內容貼到 json formatter 來重整格式,方便閱讀

範例說明:
1. 函式 json_object_put() 用來釋放 json_object 配置的記憶體,呼叫原則是
    a. 函式 json_object_from_file() 讀取 json 檔時產生的 json_object
    b. 使用函式
        json_object_new_object(),
        json_object_new_boolean(),
        json_object_new_string()
        json_object_new_int(),
        json_object_new_double(),
        json_object_new_array()
        產生的 json_object,但是如果有呼叫
        json_object_object_add(),
        json_object_array_add()
        等函式則不用再呼叫 json_object_put(),
        因為 json_object 已經被加入到另一個 json_object (例如底下範例的 json_obj) 中

範例:

#include<stdio.h>
#include<json-c/json.h>

#define EXAMPLE_JSON "./example.json"
#define NULL_NULL "NULL"
#define BOOL_BOOLEAN "BOOLEAN"
#define STR_STRING "STRING"
#define NUM_INTEGER "NUMBER"
#define NUM_DOUBLE "DOUBLE"
#define ARR_ARRAY "ARRAY"
#define OBJ_OBJECT "OBJECT"
#define NULL_MEMBER_NULL "MEMBER_NULL"
#define BOOL_MEMBER_BOOLEAN "MEMBER_BOOLEAN"
#define STR_MEMBER_STRING "MEMBER_STRING"
#define NUM_MEMBER_INTEGER "MEMBER_NUMBER"
#define NUM_MEMBER_DOUBLE "MEMBER_DOUBLE"
#define ARR_MEMBER_ARRAY "MEMBER_ARRAY"

int write_to_json_file(char* filename)
{
int ret = 0;
json_object *json_obj = NULL;
json_object *array_obj = NULL;
json_object *new_obj = NULL;
json_object *tmp_obj = NULL;
json_object *tmp1_obj = NULL;

//new a base object
json_obj = json_object_new_object();
if (!json_obj)
{
printf("Cannot create object\n");
ret = -1;
goto error;
}

//new a null
json_object_object_add(json_obj, NULL_NULL, NULL);

//new a boolean
tmp_obj = json_object_new_boolean(1);
if (!tmp_obj)
{
printf("Cannot create boolean object for %s\n", BOOL_BOOLEAN);
ret = -1;
goto error;
}
json_object_object_add(json_obj, BOOL_BOOLEAN, tmp_obj);
tmp_obj = NULL;

//new a string
tmp_obj = json_object_new_string("string");
if (!tmp_obj)
{
printf("Cannot create string object for %s\n", STR_STRING);
ret = -1;
goto error;
}
json_object_object_add(json_obj, STR_STRING, tmp_obj);
tmp_obj = NULL;

//new a integer
tmp_obj = json_object_new_int(100);
if (!tmp_obj)
{
printf("Cannot create number object for %s\n", NUM_INTEGER);
ret = -1;
goto error;
}
json_object_object_add(json_obj, NUM_INTEGER, tmp_obj);
tmp_obj = NULL;

//new a double
tmp_obj = json_object_new_double(3.1415926);
if (!tmp_obj)
{
printf("Cannot create double object for %s\n", NUM_DOUBLE);
ret = -1;
goto error;
}
json_object_object_add(json_obj, NUM_DOUBLE, tmp_obj);
tmp_obj = NULL;

//new a array
array_obj = json_object_new_array();
if (!array_obj)
{
printf("Cannot create array object\n");
ret = -1;
goto error;
}

//new a string and add to the array
new_obj = json_object_new_string("str1");
if (!new_obj)
{
printf("Cannot create string object for %s\n", ARR_ARRAY);
ret = -1;
goto error;
}
json_object_array_add(array_obj, new_obj);
new_obj = NULL;

//new a string and add to the array
new_obj = json_object_new_string("str2");
if (!new_obj)
{
printf("Cannot create string object for %s\n", ARR_ARRAY);
ret = -1;
goto error;
}
json_object_array_add(array_obj, new_obj);
new_obj = NULL;

//new a string and add to the array
new_obj = json_object_new_string("str3");
if (!new_obj)
{
printf("Cannot create string object for %s\n", ARR_ARRAY);
ret = -1;
goto error;
}
json_object_array_add(array_obj, new_obj);
new_obj = NULL;

//add array to the base object
json_object_object_add(json_obj, ARR_ARRAY, array_obj);
array_obj = NULL;

//new an object
tmp_obj = json_object_new_object();
if (!tmp_obj)
{
printf("Cannot create object\n");
ret = -1;
goto error;
}

//add a null to the object
json_object_object_add(tmp_obj, NULL_MEMBER_NULL, NULL);

//new a boolean and add to the object
tmp1_obj = json_object_new_boolean(0);
if (!tmp1_obj)
{
printf("Cannot create boolean object for %s\n", BOOL_MEMBER_BOOLEAN);
ret = -1;
goto error;
}
json_object_object_add(tmp_obj, BOOL_MEMBER_BOOLEAN, tmp1_obj);
tmp1_obj = NULL;

//new a string and add to the object
tmp1_obj = json_object_new_string("string");
if (!tmp1_obj)
{
printf("Cannot create string object for %s\n", STR_MEMBER_STRING);
ret = -1;
goto error;
}
json_object_object_add(tmp_obj, STR_MEMBER_STRING, tmp1_obj);
tmp1_obj = NULL;

//new a integer and add to the object
tmp1_obj = json_object_new_int(200);
if (!tmp1_obj)
{
printf("Cannot create number object for %s\n", NUM_MEMBER_INTEGER);
ret = -1;
goto error;
}
json_object_object_add(tmp_obj, NUM_MEMBER_INTEGER, tmp1_obj);
tmp1_obj = NULL;

//new a double and add to the object
tmp1_obj = json_object_new_double(0.123456789);
if (!tmp1_obj)
{
printf("Cannot create double object for %s\n", NUM_MEMBER_DOUBLE);
ret = -1;
goto error;
}
json_object_object_add(tmp_obj, NUM_MEMBER_DOUBLE, tmp1_obj);
tmp1_obj = NULL;

//new a array
array_obj = json_object_new_array();
if (!array_obj)
{
printf("Cannot create array object\n");
ret = -1;
goto error;
}

//new a integer and add to array
new_obj = json_object_new_int(1);
if (!new_obj)
{
printf("Cannot create string object for %s\n", ARR_MEMBER_ARRAY);
ret = -1;
goto error;
}
json_object_array_add(array_obj, new_obj);
new_obj = NULL;

//new a integer and add to array
new_obj = json_object_new_int(2);
if (!new_obj)
{
printf("Cannot create string object for %s\n", ARR_MEMBER_ARRAY);
ret = -1;
goto error;
}
json_object_array_add(array_obj, new_obj);
new_obj = NULL;

//new a integer and add to array
new_obj = json_object_new_int(3);
if (!new_obj)
{
printf("Cannot create string object for %s\n", ARR_MEMBER_ARRAY);
ret = -1;
goto error;
}
json_object_array_add(array_obj, new_obj);
new_obj = NULL;

//add array to the object
json_object_object_add(tmp_obj, ARR_MEMBER_ARRAY, array_obj);
array_obj = NULL;

//add the object to base object
json_object_object_add(json_obj, OBJ_OBJECT, tmp_obj);
tmp_obj = NULL;

//write the base object to write.json
json_object_to_file(filename, json_obj);

error:
json_object_put(tmp1_obj);
json_object_put(tmp_obj);
json_object_put(new_obj);
json_object_put(array_obj);
json_object_put(json_obj);

return ret;
}

int read_from_json_file(char *filename)
{
int ret = 0;
json_object *test_obj = NULL;
json_object *tmp_obj = NULL;
json_object *tmp1_obj = NULL;
json_object *tmp2_obj = NULL;

//get json object from file
test_obj = json_object_from_file(filename);
if (!test_obj)
{
printf("Cannot open %s\n", filename);
ret = -1;
goto error;
}

//get null
tmp_obj = json_object_object_get(test_obj, NULL_NULL);
if (tmp_obj)
{
printf("Cannot get %s object\n", NULL_NULL);
ret = -1;
goto error;
}
printf("%s = %s\n", NULL_NULL, "null");

//get boolean
tmp_obj = json_object_object_get(test_obj, BOOL_BOOLEAN);
if (!tmp_obj)
{
printf("Cannot get %s object\n", BOOL_BOOLEAN);
ret = -1;
goto error;
}
printf("%s = %s\n", BOOL_BOOLEAN, ((json_object_get_boolean(tmp_obj))?("TRUE"):("FALSE")));

//get string
tmp_obj = json_object_object_get(test_obj, STR_STRING);
if (!tmp_obj)
{
printf("Cannot get %s object\n", STR_STRING);
ret = -1;
goto error;
}
printf("%s = %s\n", STR_STRING, json_object_get_string(tmp_obj));

//get integer
tmp_obj = json_object_object_get(test_obj, NUM_INTEGER);
if (!tmp_obj)
{
printf("Cannot get %s object\n", NUM_INTEGER);
ret = -1;
goto error;
}
printf("%s = %d\n", NUM_INTEGER, json_object_get_int(tmp_obj));

//get double
tmp_obj = json_object_object_get(test_obj, NUM_DOUBLE);
if (!tmp_obj)
{
printf("Cannot get %s object\n", NUM_DOUBLE);
ret = -1;
goto error;
}
printf("%s = %F\n", NUM_DOUBLE, json_object_get_double(tmp_obj));

//get array
tmp_obj = json_object_object_get(test_obj, ARR_ARRAY);
if (!tmp_obj)
{
printf("Cannot get %s object\n", ARR_ARRAY);
ret = -1;
goto error;
}
//get the length of the array
printf("%s size = %d\n", ARR_ARRAY, json_object_array_length(tmp_obj));

//get the value of array[0]
tmp1_obj = json_object_array_get_idx(tmp_obj, 0);
printf("%s[0] = %s\n", ARR_ARRAY, json_object_get_string(tmp1_obj));

//get the value of array[1]
tmp1_obj = json_object_array_get_idx(tmp_obj, 1);
printf("%s[1] = %s\n", ARR_ARRAY, json_object_get_string(tmp1_obj));

//get the value of array[2]
tmp1_obj = json_object_array_get_idx(tmp_obj, 2);
printf("%s[2] = %s\n", ARR_ARRAY, json_object_get_string(tmp1_obj));

//get object
tmp_obj = json_object_object_get(test_obj, OBJ_OBJECT);
if (!tmp_obj)
{
printf("Cannot get %s object\n", OBJ_OBJECT);
ret = -1;
goto error;
}

//get null within a object
tmp1_obj = json_object_object_get(tmp_obj, NULL_MEMBER_NULL);
if (tmp1_obj)
{
printf("Cannot get %s object\n", NULL_MEMBER_NULL);
ret = -1;
goto error;
}
printf("%s = %s\n", NULL_MEMBER_NULL, "null");

//get boolean within a object
tmp1_obj = json_object_object_get(tmp_obj, BOOL_MEMBER_BOOLEAN);
if (!tmp1_obj)
{
printf("Cannot get %s object\n", BOOL_MEMBER_BOOLEAN);
ret = -1;
goto error;
}
printf("%s = %s\n", BOOL_MEMBER_BOOLEAN, ((json_object_get_boolean(tmp1_obj))?("TRUE"):("FALSE")));

//get string within a object
tmp1_obj = json_object_object_get(tmp_obj, STR_MEMBER_STRING);
if (!tmp1_obj)
{
printf("Cannot get %s object\n", STR_MEMBER_STRING);
ret = -1;
goto error;
}
printf("%s = %s\n", STR_MEMBER_STRING, json_object_get_string(tmp1_obj));

//get integer within a object
tmp1_obj = json_object_object_get(tmp_obj, NUM_MEMBER_INTEGER);
if (!tmp1_obj)
{
printf("Cannot get %s object\n", NUM_MEMBER_INTEGER);
ret = -1;
goto error;
}
printf("%s = %d\n", NUM_MEMBER_INTEGER, json_object_get_int(tmp1_obj));

//get double within a object
tmp1_obj = json_object_object_get(tmp_obj, NUM_MEMBER_DOUBLE);
if (!tmp1_obj)
{
printf("Cannot get %s object\n", NUM_MEMBER_DOUBLE);
ret = -1;
goto error;
}
printf("%s = %F\n", NUM_MEMBER_DOUBLE, json_object_get_double(tmp1_obj));

//get array within a object
tmp1_obj = json_object_object_get(tmp_obj, ARR_MEMBER_ARRAY);
if (!tmp1_obj)
{
printf("Cannot get %s object\n", ARR_MEMBER_ARRAY);
ret = -1;
goto error;
}
//get the length of the array within a object
printf("%s size = %d\n", ARR_MEMBER_ARRAY, json_object_array_length(tmp1_obj));

//get the value of array[0] within a object
tmp2_obj = json_object_array_get_idx(tmp1_obj, 0);
printf("%s[0] = %d\n", ARR_MEMBER_ARRAY, json_object_get_int(tmp2_obj));

//get the value of array[0] within a object
tmp2_obj = json_object_array_get_idx(tmp1_obj, 1);
printf("%s[1] = %d\n", ARR_MEMBER_ARRAY, json_object_get_int(tmp2_obj));

//get the value of array[0] within a object
tmp2_obj = json_object_array_get_idx(tmp1_obj, 2);
printf("%s[2] = %d\n", ARR_MEMBER_ARRAY, json_object_get_int(tmp2_obj));

error:
json_object_put(test_obj);

return ret;
}

int main(int argc, char *argv[])
{
int ret = 0;
ret = write_to_json_file(EXAMPLE_JSON);
if (ret != 0)
printf("Fail to write a json file\n");

ret = read_from_json_file(EXAMPLE_JSON);
if (ret != 0)
printf("Fail to read a json file\n");

return 0;
}

2018年3月6日 星期二

研究 | 檔案編碼與轉碼 file encoding

如何知道檔案的編碼是什麼?

檔案的編碼可以使用 file 指令來得知

file -bi file

範例:

file -bi /etc/inittab 

結果:

text/plain; charset=us-ascii

說明:
1. text/plain 是 MIME types,格式是 [type]/[subtype],用來表示內容的類型,text/plain 表示純文字檔。 
2. 從 charset=us-ascii 可以得知 /etc/inittab 是使用 ASCII 字元集。

如何知道系統支援哪些字元集?

透過 iconv 指令可以得知系統支援的字元集

範例:

iconv -l

結果:

The following list contain all the coded character sets known.  This does
not necessarily mean that all combinations of these names can be used for
the FROM and TO command line parameters.  One coded character set can be
listed with several different names (aliases).

  437, 500, 500V1, 850, 851, 852, 855, 856, 857, 860, 861, 862, 863, 864, 865,
  866, 866NAV, 869, 874, 904, 1026, 1046, 1047, 8859_1, 8859_2, 8859_3, 8859_4,
  8859_5, 8859_6, 8859_7, 8859_8, 8859_9, 10646-1:1993, 10646-1:1993/UCS4,
  ANSI_X3.4-1968, ANSI_X3.4-1986, ANSI_X3.4, ANSI_X3.110-1983, ANSI_X3.110,
  ARABIC, ARABIC7, ARMSCII-8, ASCII, ASMO-708, ASMO_449, BALTIC, BIG-5,
  BIG-FIVE, BIG5-HKSCS, BIG5, BIG5HKSCS, BIGFIVE, BRF, BS_4730, CA, CN-BIG5,
  CN-GB, CN, CP-AR, CP-GR, CP-HU, CP037, CP038, CP273, CP274, CP275, CP278,
  CP280, CP281, CP282, CP284, CP285, CP290, CP297, CP367, CP420, CP423, CP424,
  CP437, CP500, CP737, CP775, CP803, CP813, CP819, CP850, CP851, CP852, CP855,
  CP856, CP857, CP860, CP861, CP862, CP863, CP864, CP865, CP866, CP866NAV,
  CP868, CP869, CP870, CP871, CP874, CP875, CP880, CP891, CP901, 
...

說明:
由於字元集很多,就不全部顯示出來。

如何改變檔案的編碼?

使用 iconv 轉換檔案的編碼
此範例將檔案的檔案編碼從 utf-8 換成 us-ascii

範例:

[william@william-fedora ~]$ cat test.utf8 
此檔案使用 utf-8 編碼

[william@william-fedora ~]$ file -bi test.utf8 
text/plain; charset=utf-8

[william@william-fedora ~]$ iconv -f utf-8 -t us-ascii -o test.ascii test.utf8 

iconv: illegal input sequence at position 0

[william@william-fedora ~]$ iconv -f utf-8 -t us-ascii -c -o test.ascii test.utf8 

[william@william-fedora ~]$ cat test.ascii 
 utf-8 

[william@william-fedora ~]$ file -bi test.ascii 
text/plain; charset=us-ascii

[william@william-fedora ~]$ od -t x1 -c test.ascii 
0000000  20  75  74  66  2d  38  20  0d  0a
              u   t   f   -   8      \r  \n
0000011

說明:
1. utf-8 是 unicode 常用的編碼方式,當檔案的內容使用了 ASCII 字元集以外的字元時可以選用 utf-8 來做編碼,但是轉成 us-ascii 後,ASCII 字元集以外的字就遺失了。

2. 範例使用檔案 test.utf8,採用 utf-8 編碼,內容是此檔案使用 utf-8 編碼,包含中文字。使用 iconv 轉換成 us-ascii 時,因為 ASCII 字元集無法表示中文字,所以會有錯誤訊息,利用參數 -c 將無法表示的字元丟棄,轉換後的結果中文字都遺失了。