Google 試算表:取得單一儲存格的值 getValue()

 


如果你剛開始用 Apps Script,getValue() 大概是第一個讓你覺得「就這?」的 API。可是真正上戰場,才知道坑點滿滿:格式化跟原始值差在哪?日期為什麼少一天?合併儲存格怎麼老是讀不到?

別急,這篇會用很白話的方式帶你走一次實戰流程:先懂概念,再看範例,最後整理一份錯誤清單。

你不需要背語法,只要知道做報表、寄信、做自動化時要注意哪裡,getValue() 就會從小螺絲,變成你最順手的扳手。希望本篇文章能幫助到需要的您。


目錄

{tocify} $title={目錄} 



為什麼是 getValue()?


在 Apps Script 操作試算表時,你最常見的任務就是「讀取一個儲存格的值」。Range.getValue() 正是做這件事的直球 API。看似簡單,但牽涉到資料型態(文字/數字/日期/布林/公式)、時區、效能(單次 vs 批次呼叫)、與常見錯誤(空值、合併儲存格、保護範圍等)。理解它,等於打通資料讀寫的第一哩路。


getValue() 是什麼?它回傳什麼?

定義:

    在 Apps Script 中,Range.getValue() 會回傳目前範圍左上角那一格的原生值(非字串呈現),資料型態會依儲存格內容自動對應:

        數字:number(含小數)

        文字:string

        日期/時間:Date 物件(JS Date)

        勾選方塊:true/false(布林)

        空白:""(空字串)或 null(依情境,詳見雷點)

        公式儲存格:

           回傳公式「計算後的值」,不是字串的公式本身(要拿公式請用 getFormula())


對比:

    getValues():回傳二維陣列(可批次讀取多格,效能更好)

    getDisplayValue():回傳「使用者看到的格式化文字」(例如貨幣、日期格式)

    getDisplayValues():回傳格式化文字的二維陣列

    getRichTextValue():回傳 RichTextValue(保留超連結/粗體等樣式)

getValue() = 左上角那一格的「原始值」;如果你要一次讀很多格,請優先用 getValues()。


開工前準備:專案與授權

1.    打開你的 Google 試算表 → 上方選單 Extensions(擴充功能)→ Apps Script。

2.    第一次使用會要求授權,依提示選取帳號並允許。

3.    在編輯器中建立 Code.gs,以下範例皆為 Apps Script(JavaScript),這是操作試算表的官方腳本語言;雖然你偏好 Kotlin,但此處屬必要語言(GAS 僅支援 JS/TS)。

提醒:專案層級時區可在 Apps Script 專案 → Project Settings 設定,影響日期讀取與寫入的行為。


最小可用範例

範例 1:讀取 A1 的值

function readSingleCell() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getActiveSheet(); // 目前工作表
  const value = sh.getRange('A1').getValue();
  Logger.log(value); // 可能是字串、數字、Date、布林或空字串
}
 

範例 2:用列列號取得

function readByRowCol() {
  const sh = SpreadsheetApp.getActiveSheet();
  // getRange(row, column, numRows?, numColumns?)
  const value = sh.getRange(2, 3).getValue(); // 讀取 C2
  Logger.log(value);
}
 

範例 3:讀取命名範圍

function readNamedRange() {
  const sh = SpreadsheetApp.getActiveSheet();
  const value = sh.getRange('myTotalCell').getValue(); // 命名範圍
  Logger.log(value);
}
 


資料型態與格式的眉角

文字 vs 數字

        儲存格中 123 可能被視為 number,若你要「以字串」讀取(保留前導零等),請在試算表先設為文字格式,或改用 getDisplayValue()。

        例:SKU 00123,getValue() 會掉前導零;getDisplayValue() 保留為 "00123"。

日期/時間(Date 物件)

        getValue() 回傳 Date,請以 Utilities.formatDate() 或 toLocaleString() 格式化。

        時區很關鍵:試算表檔案時區、Apps Script 專案時區、帳號時區不一致會造成日差。

function readDate() {
  const sh = SpreadsheetApp.getActiveSheet();
  const date = sh.getRange('B2').getValue(); // Date 物件
  const tz = Session.getScriptTimeZone();
  const text = Utilities.formatDate(date, tz, 'yyyy-MM-dd HH:mm:ss');
  Logger.log(text);
}
 

公式儲存格

    getValue() 取得「公式計算後的值」。若需要公式本身:getRange().getFormula()。

    若公式需花時間計算,建議 SpreadsheetApp.flush() 之後再讀,避免讀到舊值。

勾選方塊

    Checkbox 讀到的是 true/false。若使用者自訂勾選/未勾選的輸出文字,則不再是布林,可能變字串。

空白儲存格

    多數情況回傳 ""(空字串);個別函式計算失敗可能回 null。判斷時請以「寬鬆邏輯」處理:

const v = sh.getRange('A10').getValue();
if (v === "" || v === null) {
  // 視為空
}
 

getValue() vs getValues():效能與規模

何時用 getValue()

    單一格、偶發性讀取。

    事件觸發中只要一格參數。

何時用 getValues()

    要讀很多格或整塊範圍(例如 1000×10)。

    一次 I/O 拿回二維陣列,在記憶體迭代,效能遠勝逐格 getValue() 迴圈。

function preferBatch() {
  const sh = SpreadsheetApp.getActiveSheet();
  const range = sh.getRange(2, 1, sh.getLastRow()-1, 5); // A2:E(last)
  const values = range.getValues(); // [[...],[...],...]
  // 在記憶體中跑邏輯
  const ids = values.map(row => row[0]).filter(x => x !== "");
  Logger.log(ids.length);
}
  

原則:能批次就不要逐格。這是 GAS 大量資料處理的基本功。


情境實作:常見讀取套路

讀取表頭索引,找欄位值

function readByHeader(headerName) {
  const sh = SpreadsheetApp.getActiveSheet();
  const header = sh.getRange(1, 1, 1, sh.getLastColumn()).getValues()[0];
  const idx = header.indexOf(headerName);
  if (idx === -1) throw new Error('找不到欄位:' + headerName);
  const value = sh.getRange(2, idx+1).getValue(); // 第二列該欄
  return value;
}
  

讀取動態最後一列某欄

function readLastRowValue(colA1) {
  const sh = SpreadsheetApp.getActiveSheet();
  const lastRow = sh.getLastRow();
  if (lastRow < 1) return null;
  const col = sh.getRange(colA1 + lastRow).getColumn(); // 例如 'B' → B(lastRow)
  return sh.getRange(lastRow, col).getValue();
}
  

讀取多工作表:設定表(Config)+ 主表(Data)

function readConfigThenData() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const cfg = ss.getSheetByName('Config');
  const data = ss.getSheetByName('Data');
  const days = cfg.getRange('B2').getValue(); // 報表天數
  const start = new Date(Date.now() - days*24*3600*1000);
  const tz = Session.getScriptTimeZone();
  const values = data.getRange(2,1,data.getLastRow()-1,5).getValues();
  const filtered = values.filter(r => r[0] instanceof Date && r[0] >= start);
  Logger.log(`近 ${days} 天筆數:` + filtered.length);
}

  

讀取 Checkbox 以決定流程

function toggleByCheckbox() {
  const sh = SpreadsheetApp.getActiveSheet();
  const on = sh.getRange('D2').getValue() === true;
  if (on) {
    // do something
  } else {
    // do another thing
  }
}
  

大量讀取但仍需單格語意

先 getValues() 取回陣列,再在記憶體模擬「單格讀」的語意,避免千次 I/O。

function readManyAsSingle() {
  const sh = SpreadsheetApp.getActiveSheet();
  const range = sh.getRange(2,1,100,3);
  const values = range.getValues();
  // 假裝逐格
  values.forEach((row, i) => {
    const a = row[0]; // A
    const b = row[1]; // B
    const c = row[2]; // C
    // ...
  });
}
 

與顯示格式共舞:何時用 getDisplayValue()

   報表輸出、寄信、合約號碼、金額文字化(含千分位/幣別)等情境,請使用 getDisplayValue() 取得「看得見的樣子」。

    混用技巧:同一儲存格先用 getValue() 得到原始數值做運算,再用 getDisplayValue() 做輸出。

function valueVsDisplay() {
  const sh = SpreadsheetApp.getActiveSheet();
  const v = sh.getRange('C5').getValue();        // 1000.5
  const d = sh.getRange('C5').getDisplayValue(); // $1,000.50
  Logger.log({ raw: v, show: d });
}
 


與公式同步:flush() 與計算時序

當你剛寫入公式或資料、立刻讀取結果時,可能遇到尚未重新計算的狀態。解法:

function writeThenRead() {
  const sh = SpreadsheetApp.getActiveSheet();
  sh.getRange('E2').setFormula('=SUM(A2:D2)');
  SpreadsheetApp.flush(); // 強制將排隊動作與計算刷新
  const v = sh.getRange('E2').getValue(); // 比較可靠
  Logger.log(v);
}
 


錯誤處理與例外情境

常見錯誤訊息與狀況

狀況 症狀 可能原因 解法
讀到空字串 "" 儲存格真的空、或合併格非左上角 確認是否合併;改讀左上角;或以 getDisplayValue() 檢查
讀到 null null 公式錯誤或外掛來源還未可用 flush()、加重試;或以 getDisplayValue() 觀察畫面值
讀到 #N/A 字樣 字串 公式錯誤值會以字串回傳 視為錯誤狀態處理,不要當數值用
日期少一天 Date 物件顯示日差 時區不一致 專案與試算表時區對齊;格式化時帶入相同 tz
極慢 執行逾時 逐格 getValue() 迴圈 getValues() 批次化
權限例外 Exception 無存取權或 Web App 身分不同 正確共享、以執行者身分部署


getValue() 的 12 條黃金守則

1.    只讀一格才用 getValue();多格一定用 getValues()。

2.    要格式化樣式(幣別/日期字串)就用 getDisplayValue()。

3.    讀日期時,處理好時區與格式化。

4.    讀公式值要 flush(),避免舊值。

5.    Checkbox 讀回 true/false,自訂文字就不是布林。

6.    空值判斷同時檢查 "" 與 null。

7.    避免在資料區使用合併儲存格。

8.    欄位定位請用表頭索引,不要硬編欄號。

9.    讀大量資料→記憶體運算→一次寫回。

10.    與觸發器並用時,盡量用事件物件 e 的值,少碰即時 I/O。

11.    專案、檔案、帳號時區要一致。

12.    錯誤字串(#N/A 等)要當錯誤處理,不要當數值。


常見錯誤與雷點

值怪怪的?

    看看是 getValue() 還是 getDisplayValue()。

    公式剛寫入就讀?→ 加 SpreadsheetApp.flush()。

    合併儲存格?→ 改讀左上角或取消合併。

日期亂跳一天?

    專案與試算表時區是否一致?

    Utilities.formatDate(date, tz, 'yyyy-MM-dd') 明確指定 tz。

大量資料慢到爆?

    把所有 getValue() 迴圈改為一次 getValues()。

    只在記憶體運算,最後 setValues() 一次性寫回。

讀到 #N/A 字樣?

    把它視為錯誤訊號,不是數字。

    追本溯源修正公式或資料。

觸發器偶爾讀不到最新資料?

    flush();或從 e.values 拿當次提交值;必要時加 100~500ms 微延遲(Utilities.sleep())


問題集

Q1:getValue() 回傳的日期能直接做加減嗎?

可以。它是 Date 物件。用 getTime() 取毫秒加減,再建立新 Date。

Q2:我只想把儲存格「看起來的樣子」寄信出去?

用 getDisplayValue()(單格)或 getDisplayValues()(多格),寄文字即可。

Q3:為什麼同一格有時回 "" 有時 null?

空白多半是 ""。但若來源是函式錯誤或外部資料連結中斷,偶爾會見到 null。撐開判斷就好。

Q4:我能一次讀「不連續」的多個單格嗎?

原生 getRangeList(['A1','D5','H10']) 可取得 RangeList,但仍建議合併成最小矩形用 getValues(),再用陣列索引取值,效能更佳。


張貼留言 (0)
較新的 較舊