Otto Lin
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: 'Project documentation template' disqus: hackmd --- **多媒體訊號處理-第八組** 資料自動彙整說明文件 === # Table of Content - **xlsx轉csv檔腳本 概念說明** - **xlsx2csv.py demo** - **Unique & Sort 程式自動化排序 概念說明** - **industry_merge.c & conference_merge.c demo** - **歸納 & 總結** - **分工表** --- # xlsx轉csv檔腳本概念說明 此批量將 *.xlsx轉檔為 *.csv的程式是使用Python所書寫的程式。此處需要引用了兩個函式庫來輔助並達到數據處理的目的,這兩個函式庫分別為"pandas"以及"os"。 首先,簡單介紹一下這兩個函式庫分別的功能: - "pandas"是一個專司於資料處理和數據結構應用的函式庫,這個基於numpy的函式庫功能非常強大,可以做到excel裡的許多功能,在現世代的數據分析以及數據處理中扮演非常重要的角色。 - "os"(Operating System)是Python裡的一個標準函式庫,提供了許多與操作系統互動和文件系統操作相關的功能,例如文件路徑操作、環境變數操作、和目錄的刪除以及重新命名等等。 這邊注意到我們所使用的VScode環境中是還沒有安裝這幾個需要用到的函式庫和其擴充功能的,我們需要透過在終端機(Terminal)輸入以下兩個指令: ```python pip install pandas ``` ```python pip install openpyxl ``` 輸入且安裝完成後,我們就能在VScode的環境裡成功使用pandas函式庫裡的資料處理功能以及開啟並且讀取excel檔案了。 --- 這個程式的概念為使用迴圈來遍歷source資料夾中的所有文件,並且判讀其是否為xlsx檔後再進行轉換為csv檔的腳本。為了實現此一功能,我分別使用了幾個函式庫裡的實用函數。另外,只需要輸入存放要被轉檔的xlsx資料夾之相對路徑,以及欲存放csv檔案的資料夾相對路徑便能成功執行。以下將依序做簡單的介紹: - 使用 *os.getcwd()* 來取得當前的工作目錄。 - 使用 *os.path.join( )* 來指定輸出csv文件的完整路徑。 - 使用 *os.listdir( )* 來將指定的資料夾中遍歷。 - 使用 *pd.ExcelFile( )* 來打開Excel文件。 - 使用 *os.path.splitext( )* 來透過刪除文件擴展名並添加 '.csv'來生成轉檔後的文件名。 - 使用 *df.to_csv( )* 將DataFrame寫入指定的輸出路徑的CSV文件。 以上為將xlsx批次轉檔成csv的動作之主要概念。 接下要做的是合併所有的csv文件,並將他分組成industry & conference,以產生兩個合併的csv檔 在合併csv的動作裡,我們使用group1 和 group2 存儲不同分組(產業 & 會議)數據框的空列表。並在for迴圈中使用 os.listdir(folder_path)來讀取每一個檔案。 對於每個文件,程式檢查文件名是否以 .csv 結尾,以確保它是CSV文件。 接著使用 pd.read_csv(file_path) 讀取CSV文件,並將其存儲在一個 pandas 數據框中,並將identifier的名字修改已達到插入"貢獻組別"的標題。最後使用 "if '相關產業' in filename" 檢查文件名中是否包含特定的關鍵字以用來將其標示為其來源組別。並使用 to_csv 方法,將合併後的數據框保存到不同的CSV文件中,並印出結果。 程式碼如下所示:。 # *xlsx2csv.py* demo 程式碼如下所示: ```python= import pandas as pd import os # Input the relative path of source & target folder source_relative_path = input("請輸入source folder(存放xlsx檔案之資料夾)的相對路徑: ") target_relative_path = input("請輸入target folder(希望存放csv檔案之資料夾)的相對路徑: ") current_directory = os.getcwd() source_folder_path = os.path.join(current_directory, source_relative_path) target_folder_path = os.path.join(current_directory, target_relative_path) if not os.path.exists(source_folder_path) or not os.path.exists(target_folder_path): print("輸入的文件資料夾不存在,請重新輸入。") else: # Iterate through all files in the source folder for file_name in os.listdir(source_folder_path): if file_name.endswith(".xlsx"): # Process only .xlsx files # Read the Excel file xls = pd.ExcelFile(os.path.join(source_folder_path, file_name)) # Iterate through worksheets and convert to CSV for sheet_name in xls.sheet_names: df = pd.read_excel(xls, sheet_name=sheet_name) # Generate the output CSV file name output_name = f"{os.path.splitext(file_name)[0]}_{sheet_name}.csv" output_path = os.path.join(target_folder_path, output_name) # Write to the CSV file df.to_csv(output_path, index=None, header=True) else: print(f"Skipping {file_name} as it is not an .xlsx file\n") # Merge CSV files folder_path = os.path.join(current_directory, target_relative_path) # Lists to store dataframes for different groups group1 = [] group2 = [] # Iterate through files in the folder for filename in os.listdir(folder_path): if filename.endswith('.csv'): # Read CSV file into a pandas dataframe file_path = os.path.join(folder_path, filename) df = pd.read_csv(file_path) # Extract the first three characters from the filename as an identifier identifier = filename[:3] df['資料貢獻組別'] = identifier # Group dataframes based on filename or other identifiers if '相關產業' in filename: group1.append(df) else: group2.append(df) # Concatenate dataframes for the two groups merged_group1 = pd.concat(group1, ignore_index=True) merged_group2 = pd.concat(group2, ignore_index=True) # Specify different output file paths for the merged dataframes output_file_path1 = 'merged_industry.csv' output_file_path2 = 'merged_conference.csv' # Save the merged dataframes to different CSV files merged_group1.to_csv(output_file_path1, index=False) merged_group2.to_csv(output_file_path2, index=False) print("Converted xlsx to csv successfully!\n") print(f"各組industry檔案已合併到: {output_file_path1}") print(f"各組conference檔案已合併到: {output_file_path2}\n") ``` **以下兩圖分別為執行檔案前的source folder和target folder。** Source: ![](https://hackmd.io/_uploads/S1kiUSQeT.png) Target: ![](https://hackmd.io/_uploads/BJvQPrmx6.png) --- **以下兩圖分別為執行 *xlsx2csv.py* 後的output和target folder。** Output: ![](https://hackmd.io/_uploads/H1M-tr7lp.png) Target folder after run code: ![](https://hackmd.io/_uploads/Syp3qr7xa.png) 合併後產生的兩組merged CSV檔案: ![image.png](https://hackmd.io/_uploads/rkNDOhVQp.png) ## 反思 我所完成的第一版程式中是能成功轉檔的,但是出現了一個問題那就是<font color="#f00">無法將所有的工作表都轉成csv檔,而且只能把首個工作表轉換成功而已</font>。 發覺此問題後我立即做出了修正,解決方法為再寫一個迴圈並透過讀取sheet_name來正確讀取所有的工作表,最後在轉檔後的檔名上賦予其原本工作表的名子。 --- # Unique & Sort 概念說明 程式概念說明 --- 我們程式主要分為兩個conference_merge.c以及industry_merge.c,這兩個程式的內容幾乎相同差別只在於輸入程式一個是會議而另一個是產業,我們的邏輯是以處理CSV(逗號分隔值)文件為目的,並且消除重複的行,再根據它們的第二列的值對unique完成的row進行排序,然後將排序後的unique row寫入一個新的CSV文件。以下是程式碼中的主要步驟和概念: (1)Data Structures: 程式碼使用hash table資料結構來有效地存儲和管理uniaue row。Hash table的每個bucket對應於一個unique row,並且使用linked lists在每個bucket內部處理hash collisions。 (2)Hashing: 實現了一個自定義hash函數,用於計算其內容每行的hash value。該hash value用於決定row應該存儲在hash table的哪個bucket(index)中。 (3)Eliminating Duplicates: 程式碼逐行讀取輸入的CSV文件,刪除newline字元,並將每一行(行)插入hash table中。重複的行會自動被消除,因為程式碼在插入之前會檢查hash table中是否存在該行。如果已經存在,則不會再次插入。 (4)Sorting: 在處理完輸入文件後,程式碼將所有唯一的行從hash table中收集到一個陣列中。它使用qsort函數來根據它們第二列的值對唯一行的陣列進行排序。這是使用比較函數(compare_rows)並輸入qsort來完成的。 (5)Output: 排序後的unique row(不包括重複的行)被寫入一個新的CSV文件,保留了原始輸入CSV的標頭行。 (6)Memory Management: 程式碼還負責對於row以及node的動態記憶體配置,並在不再需要時將這些內存空間釋放,以確保沒有內存洩漏。 (7)Summary: 此程式碼的主要思考邏輯是有效處理和操作CSV數據,刪除重複項並根據特定列進行排序,然後將結果存儲在一個新的CSV文件中。hash資料結構的使用幫助我們更有效率的消除重複資料,而排序步驟則確保輸出數據以指定的順序進行排序。 ## 程式碼以及解釋 這個程式的主要目的是讀取一個CSV檔案,然後將其中的行進行去重和排序,最後將結果寫入到另一個CSV檔案中。我們將逐行解釋程式碼如何實現這些功能: (一) 包含標頭文件和定義常數: ``` c= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #define TABLE_SIZE 1000 ``` 這裡包含了必要的標頭文件,並定義了 TABLE_SIZE 常數,用於定義哈希表的大小。 (二) 定義 Node 結構和 HashTable 結構: ``` c= // Node structure for the linked list in the hash table typedef struct Node { char *row; struct Node *next; } Node; // Hash table structure to store unique elements typedef struct { Node *table[TABLE_SIZE]; } HashTable; ``` 這裡定義了兩個結構,Node 用於表示哈希表中的連結串列中的節點,HashTable 用於表示哈希表,其中包含一個固定大小的 table 數組。 (三)定義哈希函數 "hash": ``` c= // Hash function to calculate the index unsigned int hash(const char *key) { unsigned int hash = 0; while (*key) { hash = (hash * 31) + *key; key++; } return hash % TABLE_SIZE; } ``` 這個哈希函數接受一個字符串作為輸入,然後計算出一個哈希值,最後將哈希值對 TABLE_SIZE 取模,以確定在哈希表中的位置。 (四)定義清理函數 "clean_csv_row": ``` c= // Function to remove double quotes and spaces from a CSV row void clean_csv_row(const char *input, char *output) { int j = 0; for (int i = 0; input[i]; i++) { if (input[i] != '"' && input[i] != ' ') { output[j] = input[i]; j++; } } output[j] = '\0'; } ``` 這個函數用於清理CSV資料行,它接受一個輸入 input 和一個輸出 output,然後過濾 input 中的雙引號和空格,並將結果存儲在 output 中。 (五)定義比較函數 "compare_rows": ``` c= int compare_rows(const void *a, const void *b) { const char *row1 = *(const char **)a; const char *row2 = *(const char **)b; // Function to remove double quotes and spaces char cleaned_row1[1024]; char cleaned_row2[1024]; clean_csv_row(row1, cleaned_row1); clean_csv_row(row2, cleaned_row2); // Compare the cleaned second columns of the rows return strcmp(cleaned_row1, cleaned_row2); } ``` 這是用於 qsort 函數的比較函數,它接受兩個指向CSV行的指針 a 和 b,將它們轉換為 char 類型的指針,然後對它們的第二列進行比較。在比較之前,它會使用 clean_csv_row 函數來去除雙引號和空格,以防止造成比較錯誤。 (六)定義insert函數 "insert": ``` c= // Function to insert a row into the hash table void insert(HashTable *hashTable, const char *row) { unsigned int index = hash(row); // Remove double quotes and spaces from the row char cleaned_row[1024]; clean_csv_row(row, cleaned_row); // Check if the cleaned row already exists in the linked list Node *current = hashTable->table[index]; while (current != NULL) { char cleaned_current_row[1024]; clean_csv_row(current->row, cleaned_current_row); if (strcmp(cleaned_current_row, cleaned_row) == 0) { return; // Row already exists, so don't insert it again } current = current->next; } // Insert the new row into the linked list Node *new_node = malloc(sizeof(Node)); if (new_node == NULL) { fprintf(stderr, "Memory allocation error\n"); exit(EXIT_FAILURE); } new_node->row = strdup(row); new_node->next = hashTable->table[index]; hashTable->table[index] = new_node; } ``` 這是一個函數,用於將一行CSV資料插入到哈希表中。它首先計算 row 的哈希值,然後檢查該行是否已經存在於哈希表的連結串列中。如果不存在,它會將該行插入連結串列。 (七)定義打印唯一排序行到CSV文件的函數 "print_sorted_unique_rows_to_csv": ``` c= // Function to print the sorted unique rows to a CSV file void print_sorted_unique_rows_to_csv(HashTable *hashTable, const char *inputFile, const char *outputFile) { FILE *input = fopen(inputFile, "r"); if (input == NULL) { perror("Error opening input file"); exit(EXIT_FAILURE); } FILE *output = fopen(outputFile, "w"); if (output == NULL) { perror("Error opening output file"); exit(EXIT_FAILURE); } // Read and write the header row (first row) directly char header[1024]; if (fgets(header, sizeof(header), input)) { fputs(header, output); } // Create an array to store unique rows char *unique_rows[TABLE_SIZE]; int unique_count = 0; char line[1024]; while (fgets(line, sizeof(line), input)) { // Remove newline character at the end of the line line[strcspn(line, "\n")] = '\0'; // Insert the entire row into the hash table insert(hashTable, line); } fclose(input); // Iterate through the hash table and collect unique rows for (int i = 0; i < TABLE_SIZE; i++) { Node *current = hashTable->table[i]; while (current != NULL) { unique_rows[unique_count] = current->row; unique_count++; current = current->next; } } // Sort the array of unique rows based on the second column qsort(unique_rows, unique_count, sizeof(char *), compare_rows); // Write sorted unique rows to the CSV file for (int i = 0; i < unique_count; i++) { fprintf(output, "%s\n", unique_rows[i]); } fclose(output); } ``` 這個函數執行以下操作: 1.打開輸入和輸出文件:使用 fopen 函數分別打開輸入文件(inputFile)和輸出文件(outputFile)。如果打開輸入或輸出文件失敗,則使用 perror 函數顯示錯誤消息並退出程式。 2.讀取並寫入標頭行:使用 fgets 函數讀取輸入文件的第一行,也就是CSV文件的標頭行,並將其存儲在 header 陣列中。接著使用 fputs 函數將標頭行寫入輸出文件,以確保輸出文件保留了原始文件的標頭。 3.創建陣列以存儲唯一行:宣告了一個 char * 類型的陣列 unique_rows,這個陣列將用於存儲唯一的CSV行。創建一個整數變數 unique_count,用於跟蹤存儲的唯一行數量。 4.處理CSV文件的每一行:使用 fgets 函數遍歷輸入文件的每一行,每次讀取一行CSV資料並存儲在 line 陣列中。使用 strcspn 函數來去除 line 最後的換行符(newline character),以確保行的結尾正確。 5.插入行到哈希表:呼叫 insert 函數,將 hashTable 和 line 作為參數,以將 line 插入到哈希表中。insert 函數會確保只有唯一的行被插入,去除了重複的行。 6.關閉輸入文件:使用 fclose 函數關閉輸入文件,因為已經處理完了它。 7.收集唯一行到 unique_rows 陣列:使用迴圈遍歷哈希表中的所有節點,收集唯一行到 unique_rows 陣列中,同時增加 unique_count。 8.對 unique_rows 陣列排序:使用 qsort 函數對 unique_rows 陣列中的行進行排序,排序的方式是根據第二列的內容,使用 compare_rows 函數來比較。 9.寫入排序後的行到輸出文件:使用迴圈遍歷 unique_rows 陣列,每次寫入一行到輸出文件,然後插入換行符以分隔行。 10.關閉輸出文件:使用 fclose 函數關閉輸出文件,以確保結果被寫入文件。 (八)主函數 "main": ``` c= int main() { // Initialize the hash table HashTable hashTable; for (int i = 0; i < TABLE_SIZE; i++) { hashTable.table[i] = NULL; } // Input and output file paths const char *inputFile = "merged_conference.csv"; const char *outputFile = "output_conference.csv"; // Print the sorted unique rows (skipping header) to a new CSV file print_sorted_unique_rows_to_csv(&hashTable, inputFile, outputFile); // Free memory for (int i = 0; i < TABLE_SIZE; i++) { Node *current = hashTable.table[i]; while (current != NULL) { Node *temp = current; current = current->next; free(temp->row); free(temp); } } return EXIT_SUCCESS; } ``` 主函數初始化了哈希表,然後指定輸入和輸出文件的路徑,並呼叫 "print_sorted_unique_rows_to_csv" 函數來執行去重復和排序操作。最後,它釋放了用於存儲行的內存,並退出程序。 (九)補充:定義去除括弧函數(僅用於conference_merge.c) "remove_left_parentheses",因為我們發現conference資料中有括弧會影響排序,我們想出的解法是直接刪除後再排序: ``` c= void remove_left_parentheses(char *str) { char *src, *dst; for (src = dst = str; *src != '\0'; src++) { if (*src != '(') { *dst++ = *src; } } *dst = '\0'; } ``` 這個函式本質上執行一個"過濾"操作,從輸入字串中刪除所有左括號的實例,只保留不是左括號的字符。修改後的字串存儲在原始字串的相同內存位置,並以空字符結束。 - 總結 這個程式通過使用哈希表實現去重復,然後對唯一行進行排序,最終將排序後的結果寫入到輸出CSV文件中。這兩個主要部分相互協作,實現了對CSV文件的去重和排序。 # *industry_merge.c* & *conference_merge.c* demo 下圖為成功執行*industry_merge.c*後的排序結果,並儲存於新檔案output_industry.csv ![image.png](https://hackmd.io/_uploads/rJYZwA4ma.png) --- 下圖為成功執行*conference_merge.c*後的排序結果,並儲存於新檔案output_conference.csv ![image.png](https://hackmd.io/_uploads/rkwkDCVmT.png) 從以上兩圖我們可以觀察到經由執行unique & sort的程式之後,各組的CSV資料皆已經成功被排序,由英文大小寫以及字母順序排序,便達成了我們此次的目的。 # 歸納 & 總結 本次作業的所有程式demo過程提供於下: https://youtu.be/35c751rffUc?si=rQR7L7ipnq9vsmfW --- 這次是多媒體訊號處理的第一項團隊project,也是大學生涯裡的第一次團隊程式作業,因此我們在分配分工合作上花費了不少的時間。在共同協作方面,我們善用了Visual Studio code裡的擴充軟件 -liveshare。有了這項工具的幫忙我們在共同協作上變得方便許多,另外這次也是我們第一次使用HackMD來書寫報告,不得不說真的比word方便許多,也有十分強大的共同編輯功能和簡潔的排版,期待未來能夠更活用此項工具。 雖然花費了許多時間來完成這次的程式,但實際看到他能夠運行的感覺實在是太好了!唯一的小遺憾是我們無法使用C語言去成功讀到本機directory,希望未來的我們已經有足夠的能力來解決這個問題! # 分工表 | 工作事項/組員 | 林紘毅 | 陳品鈞 | 黃家棋 | 張庭愷 | 柯宣卉 | | ------------------ | ------ | ------ | ------ | --- | --- | | xlsx2csv.py 程式 | ✔ | | | | | | xlsx2csv.py 報告 | ✔ | | | | | | CSV_merge.py 程式 | ✔ | | | | | | CSV_merge.py 報告 | ✔ | | | | | | Unique & Sort 程式 | | ✔ | ✔ | | | | Unique & Sort 報告 | | ✔ | ✔ | | | | 歸納 & 總結demo | ✔ | | | | | | Excel資料查詢 | ✔ | ✔ | ✔ | ✔ | ✔ |

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully