You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同功能下TypeScript实现为何比Rust实现效率更高?

账单文本解析:Rust与TypeScript实现对比(Tauri项目)

需求说明

实现函数读取预加载的账单.txt文件内容,将每行交易解析为指定数据结构后返回。交易行格式示例:2023/1/7 15:14 - goods1x2 goods2x2 : 24.19 dollars,需映射到Item和Transaction结构。

在Tauri项目中完成双语言实现后,性能测试结果如下:

  • 处理100万行数据:TypeScript耗时约2秒,Rust耗时约9秒
  • 仅处理1行数据:TypeScript仍比Rust更快

Rust实现

数据结构

struct Item {
  content: String,
  count: String,
  price: String,
}

struct Transaction {
  date: String,
  time: String,
  status: i32,
  total_amount: String,
  account: String,
  items: Vec<Item>,
}

解析函数

pub fn text_bill_analysis(text: String) -> tauri::ipc::Response {
  let start = std::time::Instant::now();

  let mut transactions: Vec<Transaction> = Vec::new();
  let re = Regex::new(r"(paypay|bank2|income)").unwrap();

  for line in text.lines() {
    let line = match line.find("--") {
      Some(x) => line.split_at(x).0,
      None => line,
    };
    let line = line.trim();
    if line.is_empty() {
      continue;
    }

    let parts: Vec<&str> = line.split_whitespace().collect();
    if parts.len() < 2 {
      continue;
    }

    let date_parts: Vec<&str> = parts[0].split('/').collect();
    if date_parts.len() != 3 {
      continue;
    }
    let year = date_parts[0];
    let month = format!("{:02}", date_parts[1].parse::<i32>().unwrap_or(0));
    let day = format!("{:02}", date_parts[2].parse::<i32>().unwrap_or(0));
    let date = format!("{}/{}/{}", year, month, day);

    let time_parts: Vec<&str> = parts[1].split(':').collect();
    if time_parts.len() != 2 {
      continue;
    }
    let hour = format!("{:02}", time_parts[0].parse::<i32>().unwrap_or(0));
    let minute = format!("{:02}", time_parts[1].parse::<i32>().unwrap_or(0));
    let time = format!("{}:{}", hour, minute);

    let status = if line.contains("income") {
      1
    } else if line.contains("balance") {
      2
    } else {
      0
    };

    let unit = parts.last().unwrap_or(&"");
    if *unit != "dollars" {
      continue;
    }

    let total_amount = match parts.iter().nth_back(1) {
      Some(x) => format!("{:.2}", x.parse::<f64>().unwrap_or(0.0)),
      None => continue,
    };

    let account = if line.contains("paypay") {
      "paypay"
    } else if line.contains("bank2") {
      "bank2"
    } else {
      "bank1"
    };

    let mut items: Vec<Item> = Vec::new();
    let parts_split: Vec<&str> = line.split(" - ").collect();
    if parts_split.len() < 2 {
      continue;
    }
    let parts_colon: Vec<&str> = parts_split[1].split(" : ").collect();
    if parts_colon.len() < 2 {
      continue;
    }

    let parts_filtered: Vec<&str> = parts_colon[0]
      .split_whitespace()
      .filter(|&x| !re.is_match(x))
      .collect();

    if parts_filtered.is_empty() {
      items.push(Item {
        content: "income".to_string(),
        count: "1".to_string(),
        price: "null".to_string(),
      });
    }

    let n = parts_filtered.len() as i32;
    for part in parts_filtered {
      let cleaned_part = re.replace_all(part, "").trim().to_string();
      if cleaned_part.is_empty() {
        continue;
      }

      let item_parts: Vec<&str> = cleaned_part.split('x').collect();
      let content = item_parts[0].to_string();
      let count = item_parts
        .get(1)
        .map_or(1, |x| x.parse::<i32>().unwrap_or(1))
        .to_string();

      items.push(Item {
        content,
        count,
        price: if n == 1 {
          total_amount.clone()
        } else {
          "null".to_string()
        },
      });
    }

    transactions.push(Transaction {
      date,
      time,
      status,
      total_amount,
      account: account.to_string(),
      items,
    });
  }

  let elapsed = start.elapsed();
  println!("rust: {:?}", elapsed);
  println!("len: {:?}", transactions.len());

  tauri::ipc::Response::new(serde_json::to_string(&transactions).unwrap())
}

TypeScript实现

数据结构

interface Item {
  content: string
  count: string
  price: string
}

interface Transaction {
  date: string
  time: string
  status: number
  totalAmount: string
  account: string
  items: Item[]
}

解析函数

function textBillAnalysisTs(text: string): Transaction[] {
  console.time('ts')

  const transactions: Transaction[] = []
  const lines = text.split('\n')

  for (const _line of lines) {
    const lineWithoutComment = _line.split('--')[0]
    const line = lineWithoutComment.trim()
    if (line === '') continue

    const parts = line.split(/\s+/)
    if (parts.length < 3) continue

    const dateParts = parts[0].split('/')
    if (!dateParts || dateParts.length !== 3) continue

    const year = dateParts[0]
    const month = dateParts[1].padStart(2, '0')
    const day = dateParts[2].padStart(2, '0')
    const date = `${year}/${month}/${day}`

    const timeParts = parts[1].split(':')
    if (!timeParts || timeParts.length !== 2) continue

    const hour = timeParts[0].padStart(2, '0')
    const minute = timeParts[1].padStart(2, '0')
    const time = `${hour}:${minute}`

    const status =
      line.includes('income')
        ? 1
        : line.includes('balance')
          ? 2
          : 0

    const unit = parts[parts.length - 1]
    if (unit !== 'dollars') continue

    const totalAmount = parseFloat(parts[parts.length - 2]).toFixed(2)

    const account = line.includes('paypay')
      ? 'paypay'
      : line.includes('bank2')
        ? 'bank2'
        : 'bank1'

    const items: Item[] = []
    const partsSplit = line.split(' - ')
    const partsColon = partsSplit[1]?.split(' : ')
    if (!partsColon) continue

    const partsFiltered = partsColon[0]
      .split(/\s+/)
      .filter((x) => !['paypay', 'bank2', 'income'].includes(x))
      .map((x) => x.trim())

    if (partsFiltered.length === 0) {
      items.push({ content: 'income', count: '1', price: 'null' })
    }

    const n = partsFiltered.length
    for (const part of partsFiltered) {
      const cleanedPart = part
        .replace(/(paypay|bank2|income)/g, '')
        .trim()
      if (cleanedPart === '') continue

      const itemParts = cleanedPart.split('x')
      const content = itemParts[0] || ''
      const count = Math.max(parseInt(itemParts[1] || '1', 10), 1).toString()

      items.push({
        content,
        count,
        price: n === 1 ? totalAmount : 'null'
      })
    }

    transactions.push({
      date,
      time,
      status,
      totalAmount,
      account,
      items
    })
  }

  console.timeEnd('ts')
  return transactions
}

支持的输入示例

2023/01/7 05:04 - goods1x2 goods2 : 4240.20 dollars
2023/01/07 5:4 - goods1x2 goods2 : 4240 dollars
2023/01/07 5:4 - goods1x2 goods2 : 4240.2 dollars
2023/01/07 5:4 - goods1x2 goods2 : 4240.20 dollars
2023/01/07 5:04 - goods1x2 goods2 : 4240 dollars
2023/01/07 5:04 - goods1x2 goods2 : 4240.2 dollars
2023/01/07 5:04 - goods1x2 goods2 : 4240.20 dollars
2023/01/07 05:4 - goods1x2 goods2 : 4240 dollars
2023/01/07 05:4 - goods1x2 goods2 : 4240.2 dollars
2023/01/07 05:4 - goods1x2 goods2 : 4240.20 dollars
2023/01/07 05:04 - goods1x2 goods2 : 4240 dollars
2023/01/07 05:04 - goods1x2 goods2 : 4240.2 dollars
2023/01/07 05:04 - goods1x2 goods2 : 4240.20 dollars

内容的提问来源于stack exchange,提问作者XunJi kora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 18:48:11