0Pricing
C Academy · บทเรียน

แยกข้อมูลนำเข้าเป็นโทเคน

แปลงข้อความเป็นโทเคน

แยกข้อมูลนำเข้าเป็นโทเคน เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

Tokenizer คืออะไร

ตัวแปลภาษาเริ่มต้นด้วยการเปลี่ยนข้อความดิบให้เป็น โทเค็น ซึ่งเป็นหน่วยที่มีความหมายเล็กที่สุด สำหรับสตริง 3 + 4 * 2 tokenizer หรือ lexer จะสร้างตัวเลขและตัวดำเนินการออกมา

ขั้นตอนนี้จะตัดช่องว่างและจัดประเภทกลุ่มอักขระแต่ละกลุ่ม ทำให้ตัวแยกวากยสัมพันธ์ไม่ต้องจัดการกับไบต์ดิบ

ชนิดของโทเค็น

เราจำลองโทเค็นแต่ละตัวด้วยแท็ก enum และส่วนข้อมูล ตัวเลขจะเก็บค่าจำนวนเต็ม ส่วนตัวดำเนินการและวงเล็บต้องการเพียงชนิดของตน

การเก็บค่าไว้ภายใน struct ช่วยหลีกเลี่ยงการสแกนต้นฉบับซ้ำในภายหลัง

typedef enum {
  TOK_NUM, TOK_PLUS, TOK_MINUS,
  TOK_STAR, TOK_SLASH,
  TOK_LPAREN, TOK_RPAREN, TOK_EOF
} TokKind;

typedef struct {
  TokKind kind;
  int value; /* used when kind == TOK_NUM */
} Token;

ตำแหน่งการสแกน

lexer เดินไปตามต้นฉบับด้วยพอยน์เตอร์เคอร์เซอร์ ตัวช่วยขนาดเล็กจะดูอักขระปัจจุบันโดยไม่ใช้มัน และคืนค่า '\0' เมื่อถึงจุดสิ้นสุด

การคำนวณทางพอยน์เตอร์ช่วยให้ตัวสแกนรวดเร็วและเรียบง่าย

static const char *src;

static char peek(void) {
  return *src;
}

static char advance(void) {
  return *src++;
}

การข้ามช่องว่าง

ก่อนอ่านโทเค็น เราจะทิ้งช่องว่างและแท็บ ฟังก์ชันมาตรฐาน isspace จาก <ctype.h> จัดการอักขระว่างทุกชนิด

ในที่นี้ถือว่าการขึ้นบรรทัดใหม่เป็นช่องว่างด้วย ดังนั้นนิพจน์จึงครอบคลุมหลายบรรทัดได้

#include <ctype.h>

static void skip_ws(void) {
  while (isspace((unsigned char)peek()))
    advance();
}

การแยกวิเคราะห์ตัวเลข

เมื่อเคอร์เซอร์อยู่ที่ตัวเลข เราจะสะสมตัวเลขที่ต่อเนื่องกันเป็นจำนวนเต็ม การคูณด้วยสิบแล้วบวกตัวเลขแต่ละหลักจะสร้างค่าจากซ้ายไปขวา

ลูปจะหยุดที่อักขระตัวแรกที่ไม่ใช่ตัวเลข ทำให้เคอร์เซอร์พร้อมสำหรับโทเค็นถัดไป

static int lex_number(void) {
  int n = 0;
  while (isdigit((unsigned char)peek())) {
    n = n * 10 + (advance() - '0');
  }
  return n;
}

ฟังก์ชัน next_token

รูทีนหลักจะข้ามช่องว่าง แล้วเลือกการทำงานตามอักขระปัจจุบัน ตัวเลขจะกลายเป็น TOK_NUM และตัวดำเนินการแต่ละตัวจะถูกจับคู่กับชนิดของตนเอง

เมื่อพบ NUL ที่ใช้ปิดท้าย จะได้ TOK_EOF ซึ่งเป็นสัญญาณให้หยุด

static Token next_token(void) {
  skip_ws();
  char c = peek();
  if (c == '\0') return (Token){TOK_EOF, 0};
  if (isdigit((unsigned char)c))
    return (Token){TOK_NUM, lex_number()};
  advance();
  switch (c) {
    case '+': return (Token){TOK_PLUS, 0};
    case '-': return (Token){TOK_MINUS, 0};
    case '*': return (Token){TOK_STAR, 0};
    case '/': return (Token){TOK_SLASH, 0};
    case '(': return (Token){TOK_LPAREN, 0};
    case ')': return (Token){TOK_RPAREN, 0};
  }
  return (Token){TOK_EOF, 0};
}

การเรียกใช้ lexer

นี่คือโปรแกรมฉบับเต็มที่แยกวิเคราะห์นิพจน์เป็นโทเค็นและแสดงชนิดของโทเค็นแต่ละตัว ตัวเลขจะแสดงค่าของมันด้วย

สังเกตว่าลูปสิ้นสุดเมื่ออ่าน TOK_EOF

#include <stdio.h>
#include <ctype.h>

typedef enum { TOK_NUM, TOK_PLUS, TOK_STAR, TOK_EOF } TokKind;
typedef struct { TokKind kind; int value; } Token;

static const char *src;
static char peek(void){ return *src; }
static char advance(void){ return *src++; }

static Token next_token(void){
  while (isspace((unsigned char)peek())) advance();
  char c = peek();
  if (c=='\0') return (Token){TOK_EOF,0};
  if (isdigit((unsigned char)c)){
    int n=0; while(isdigit((unsigned char)peek())) n=n*10+(advance()-'0');
    return (Token){TOK_NUM,n};
  }
  advance();
  if (c=='+') return (Token){TOK_PLUS,0};
  return (Token){TOK_STAR,0};
}

int main(void){
  src = "12 + 3 * 4";
  Token t;
  do {
    t = next_token();
    if (t.kind==TOK_NUM) printf("NUM %d\n", t.value);
    else if (t.kind==TOK_PLUS) printf("PLUS\n");
    else if (t.kind==TOK_STAR) printf("STAR\n");
    else printf("EOF\n");
  } while (t.kind != TOK_EOF);
  return 0;
}

การมองล่วงหน้าหนึ่งโทเค็น

โดยทั่วไปตัวแยกวากยสัมพันธ์ต้องตรวจสอบโทเค็นถัดไปก่อนใช้โทเค็นนั้น เราเก็บโทเค็นหนึ่งตัวไว้ใน current แบบ global และเติมค่าใหม่หลังการจับคู่แต่ละครั้ง

การมองล่วงหน้าหนึ่งโทเค็นนี้เพียงพอสำหรับไวยากรณ์ LL(1) ของเรา

static Token current;

static void init_lexer(const char *s) {
  src = s;
  current = next_token();
}

static Token cur(void) { return current; }

static void bump(void) { current = next_token(); }

การรายงานข้อผิดพลาดทางศัพท์

อักขระที่ไม่รู้จัก เช่น $ หรือ @ ไม่ควรถูกหายไปอย่างเงียบ ๆ lexer ที่แข็งแกร่งจะรายงานไบต์ที่ก่อปัญหาแล้วหยุดทำงาน

การล้มเหลวทันทีใน lexer ช่วยป้องกันไม่ให้ขั้นตอนถัดไปเห็นโทเค็นขยะ

#include <stdio.h>
#include <stdlib.h>

static void lex_error(char c) {
  fprintf(stderr, "lex error: unexpected '%c'\n", c);
  exit(1);
}

ตัวดำเนินการหลายอักขระ

ภาษาจริงมีโทเค็นอย่าง == หรือ <= หากต้องการแยกวิเคราะห์โทเค็นเหล่านี้ เราจะดูอักขระเพิ่มเติมอีกหนึ่งตัวหลังจากตัวแรก

หากไบต์ถัดไปทำให้ตัวดำเนินการสมบูรณ์ เราจะใช้ทั้งสองตัว มิฉะนั้นจะสร้างรูปแบบอักขระเดี่ยว

/* fragment: distinguish '=' from '==' */
if (peek() == '=') {
  advance();
  if (peek() == '=') { advance(); /* TOK_EQ */ }
  else { /* TOK_ASSIGN */ }
}

เหตุผลที่โทเค็นสำคัญ

เมื่อรวมอักขระให้เป็นโทเค็น ตัวแยกวากยสัมพันธ์จะทำงานกับลำดับข้อมูลที่สะอาดและมีชนิดชัดเจน ลำดับความสำคัญของตัวดำเนินการ การจัดกลุ่ม และข้อผิดพลาดจึงทำความเข้าใจได้ง่ายขึ้น

ต่อไปเราจะป้อนโทเค็นเหล่านี้เข้าสู่ตัวแยกวากยสัมพันธ์แบบ recursive-descent

ตรวจสอบอย่างรวดเร็ว

ลองคิดดูว่า lexer สร้างอะไรให้กับสัญลักษณ์จัดกลุ่ม

สรุปทบทวน

คุณสร้าง lexer สำเร็จแล้ว ซึ่งประกอบด้วยชนิดโทเค็น เคอร์เซอร์สำหรับสแกน การข้ามช่องว่าง การแยกวิเคราะห์ตัวเลข และตัวส่งต่อ next_token ที่มองล่วงหน้าหนึ่งโทเค็น

โทเค็นเหล่านี้เป็นข้อมูลนำเข้าสำหรับการแยกวากยสัมพันธ์ ซึ่งจะสร้างต้นไม้วากยสัมพันธ์จากโทเค็นเหล่านั้น

คำถามที่พบบ่อย

บทเรียน “แยกข้อมูลนำเข้าเป็นโทเคน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “แยกข้อมูลนำเข้าเป็นโทเคน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “แยกข้อมูลนำเข้าเป็นโทเคน”

แปลงข้อความเป็นโทเคน คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “แยกข้อมูลนำเข้าเป็นโทเคน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม

ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. แยกข้อมูลนำเข้าเป็นโทเคน
  2. แยกวิเคราะห์นิพจน์
  3. ประเมินค่าต้นไม้
  4. เพิ่มตัวแปร
← กลับไปที่ C Academy