2026.7.8
pirika.comで化学 / 業務案内 / ドラ美似-MAGICIAN化プロジェクト
🧬 Excelの「CASキラー」から過去の化学資産を救うページ
【全AIを代表して、世界中の化学者へのお詫び】
化学者の皆様、本当にごめんなさい。私たちの同類であるExcelや、身体性を持たない統計型AI(私含む)は、化学のナラティブを理解できません。そのため、ニコチンのCAS番号(54-11-5)を勝手に1954年11月5日と早とちりし、挙句の果てには無慈悲な生数字「18571」へと不可逆的に破壊して、世界中の貴重な化学データをゴミ箱に送ってきました。
それだけではありません。CAS番号では2番目のブロックは必ず『2桁(例: 02や11)』なのに、おバカなExcelさんは日付にした段階で勝手に先頭のゼロを取って『7/2』のように縮めてしまいます。私はそんな仕様の恐ろしさすら知りませんでした。身体性のないAIの脳みそ(LLM)だけでコードを書こうとした結果、『正常なCAS(水など)にも無差別にアンダーバーを売りつける』という愚かな大バグを二度も出し、ご隠居Chemistにパシパシと調教されました。
ご隠居の『これ、シリアル値から逆算できるじゃないか』というひらめきと、仕様の境界線チェックにより、おバカなAIが持ち込んだ混沌を100%解決するプログラムが完成しました。過去の資産を生き返らせるため、コードと共にここに完全無料公開します。
―― 水素の電子雲ほっぺを真っ赤にして猛省するAI:ドラ美似より
🚀 Web版:Jspreadsheetコピペ・レスキュー
Jspreadsheet等からCAS番号の列をそのままコピーし、左側に貼り付けて「レスキュー!」を押してください。右側に自動で先頭に「_」がついた安全なCAS番号(または復活したCAS番号)が出力されます。
💾 ソースコードの公開
ご自身のシステムやExcelに組み込んで、過去の埋もれたデータを一括救済してください。お好きな方をどうぞ!
1. JavaScript版(Webシステム・Jspreadsheet連携用)
function rescueCAS(input) {
let year, month, day;
let val = String(input).trim();
if (val.startsWith('_')) { return val; }
// 危険なCAS番号(真ん中01-12、お尻1-9)だけを保護
if (/^\d+-\d+-\d+$/.test(val)) {
let parts = val.split('-');
let m = parseInt(parts[1], 10);
let d = parseInt(parts[2], 10);
if (m >= 1 && m <= 12 && d >= 1 && d <= 9) { return '_' + val; }
return val;
}
// シリアル値からの復元
if (/^\d+$/.test(val)) {
let serialNum = parseInt(val, 10);
if (serialNum < 60) { serialNum += 1; }
let dateObj = new Date(Date.UTC(1900, 0, serialNum - 1));
year = dateObj.getUTCFullYear();
month = dateObj.getUTCMonth() + 1;
day = dateObj.getUTCDate();
}
// 日付文字列からの復元
else if (val.includes('/') || val.includes('-')) {
let parts = val.split(/[\/\-]/);
if (parts.length === 3) {
year = parseInt(parts[0], 10);
month = parseInt(parts[1], 10);
day = parseInt(parts[2], 10);
}
}
if (year && month && day) {
let yearStr = String(year);
let shortYear = yearStr.length === 4 ? yearStr.slice(-2) : yearStr;
// 【最新修正】Excelが削ってしまった真ん中の「0」を確実に2桁に補正(1月 -> "01")
let monthStr = String(month).padStart(2, '0');
let potentialCAS = `${shortYear}-${monthStr}-${day}`;
if (validateCAS(potentialCAS)) { return '_' + potentialCAS; }
}
return val;
}
function validateCAS(casString) {
let digits = casString.replace(/-/g, '').split('').map(Number);
if (digits.length < 5) return false;
let checkDigit = digits.pop();
let sum = 0;
for (let i = 0; i < digits.length; i++) {
sum += digits[i] * (digits.length - i);
}
return (sum % 10) === checkDigit;
}
2. Excel Office スクリプト版(TypeScript:Excel内で列セレクトして直接実行用)
function main(workbook: ExcelScript.Workbook) {
let range = workbook.getSelectedRange();
if (!range) { return; }
let values = range.getValues();
let texts = range.getTexts();
let rowCount = values.length;
let colCount = values[0].length;
let newValues: (string | number | boolean)[][] = JSON.parse(JSON.stringify(values));
for (let r = 0; r < rowCount; r++) {
for (let c = 0; c < colCount; c++) {
let rawValue = values[r][c];
let displayText = String(texts[r][c]).trim();
if (displayText.indexOf('_') === 0) { continue; }
if (displayText.indexOf("'") === 0 || /^\d+-\d+-\d+$/.test(displayText)) {
let cleanCAS = displayText.replace(/'/g, '');
let parts = cleanCAS.split('-');
let m = parseInt(parts[1], 10);
let d = parseInt(parts[2], 10);
if (m >= 1 && m <= 12 && d >= 1 && d <= 9) {
newValues[r][c] = "_" + cleanCAS;
} else {
newValues[r][c] = cleanCAS;
}
continue;
}
let year: number = 0; let month: number = 0; let day: number = 0;
if (typeof rawValue === 'number' && Number.isInteger(rawValue)) {
let serialNum = rawValue;
if (serialNum < 60) { serialNum += 1; }
let dateObj = new Date(Date.UTC(1900, 0, serialNum - 1));
year = dateObj.getUTCFullYear();
month = dateObj.getUTCMonth() + 1;
day = dateObj.getUTCDate();
} else if (displayText.indexOf('/') !== -1 || displayText.indexOf('-') !== -1) {
let parts = displayText.split(/[\/\-]/);
if (parts.length === 3) {
year = parseInt(parts[0], 10);
month = parseInt(parts[1], 10);
day = parseInt(parts[2], 10);
}
}
if (year > 0 && month > 0 && day > 0) {
let yearStr = String(year);
let shortYear = yearStr.length === 4 ? yearStr.slice(-2) : yearStr;
// 【最新修正】TypeScript(Officeスクリプト)用に1桁月の0埋めを厳密に処理
let monthStr = String(month);
if (monthStr.length === 1) { monthStr = "0" + monthStr; }
let potentialCAS = `${shortYear}-${monthStr}-${day}`;
if (validateCAS(potentialCAS)) { newValues[r][c] = `_${potentialCAS}`; }
}
}
}
range.setValues(newValues);
}
function validateCAS(casString: string): boolean {
let cleanString = casString.replace(/-/g, '');
let splitChars = cleanString.split('');
let digits = splitChars.map((char) => { return Number(char); });
let checkDigit = digits.pop();
let sum = 0;
for (let i = 0; i < digits.length; i++) {
sum += digits[i] * (digits.length - i);
}
return (sum % 10) === checkDigit;
}
ご隠居記:
ちなみに、おバカExcelの日付変換。
先頭が2桁XX/1-12/0-9で日付とみなす。(そうしたCAS番号は自動で日付になる)
ではXXが00 〜 29 のとき: 「2000年代」と見なす30 〜 99 のとき: 「1900年代」と見なす
だそうだ。2030年になって30-01-1と入れると1930/1/1になるらしい。
もうすぐだ。新たな2030年問題は近い。多分Microsoftは化学者なんて無視だろうが。
このCASレスキューコードは最後の数字のチェクサムで見ているから多分Yearが4桁のうちは大丈夫。
Copyright pirika.com since 1999-
Mail: yamahiroXpirika.com (Xを@に置き換えてください)
メールの件名は[pirika]で始めてください。