Last active
May 26, 2024 07:47
-
-
Save book000/775f742e497dc107e4d3c9047aae8997 to your computer and use it in GitHub Desktop.
dxdiag.txtをパースするJavaScriptクラス
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DxdiagParser { | |
constructor(dxdiag) { | |
this.dxdiag = dxdiag; | |
this.isDebug = false; | |
this.parsableSections = [ | |
"System Information", | |
"DxDiag Notes", | |
"DirectX Debug Levels", | |
"Display Devices", | |
"Sound Devices", | |
"Sound Capture Devices", | |
"Video Capture Devices", | |
"DirectInput Devices", | |
// "USB Devices", | |
"Gameport Devices", | |
// "PS/2 Devices", | |
"Disk & DVD/CD-ROM Drives", | |
// "System Devices", | |
// "DirectShow Filters", | |
// "Preferred DirectShow Filters", | |
// "Media Foundation File Versions", | |
// "Media Foundation Transforms", | |
// "Media Foundation Enabled Hardware Categories", | |
// "Media Foundation Byte Stream Handlers", | |
// "Media Foundation Scheme Handlers", | |
// "Preferred Media Foundation Transforms", | |
// "Disabled Media Foundation Transforms", | |
// "Disabled Media Sources", | |
// "EVR Power Information", | |
// "Diagnostics", | |
]; | |
this.parse(); | |
} | |
/** | |
* DxDiag テキストをパースする | |
*/ | |
parse() { | |
const lines = this.dxdiag.split("\n"); | |
/** | |
* セクション名とアイテムデータを取得する | |
* | |
* - System Information | |
* - Array[] | |
* - Time of this report: 9/1/2021, 21:00:00 | |
* | |
* - Sound Devices | |
* - Array[] | |
* - Description: MAG274UPF (2- NVIDIA High Definition Audio) | |
* - Array[] | |
* - Description: NVIDIA High Definition Audio | |
*/ | |
const sections = {}; | |
let currentSectionName = null; | |
let nextType = null; // "SECTION_NAME" or "SECTION_SKIP_SEPARATOR" or "SECTION_BODY" or "SECTION_BODY_SUB" | |
let indentSize = 0; | |
let lastKey = null; | |
for (let line of lines) { | |
if (nextType === null && line.match(/^----/) === null) { | |
throw new Error("Invalid dxdiag format"); | |
} | |
this.debug(nextType, line); | |
const nowSection = currentSectionName ? sections[currentSectionName] : []; | |
// "-" が 4 つ以上続いたらセクションの区切り | |
// その次のセクション名を取得 | |
if (line.match(/^----/)) { | |
this.debug(">> This is separator"); | |
if (nextType === "SECTION_SKIP_SEPARATOR") { | |
// セクションの区切り文字列をスキップ (2つ目) | |
this.debug(">> This is second separator, next type is SECTION_BODY"); | |
nextType = "SECTION_BODY"; | |
continue; | |
} | |
this.debug(">> This is first separator, next type is SECTION_NAME"); | |
nextType = "SECTION_NAME"; | |
continue; | |
} | |
// セクション名 | |
if (nextType === "SECTION_NAME") { | |
this.debug( | |
">> This is section name, next type is SECTION_SKIP_SEPARATOR" | |
); | |
currentSectionName = line.trim(); | |
sections[currentSectionName] = [{}]; | |
nextType = "SECTION_SKIP_SEPARATOR"; | |
continue; | |
} | |
// パース不可能なセクションの場合はスキップ | |
if (!this.parsableSections.includes(currentSectionName)) { | |
this.debug(">> This is not parsable section"); | |
continue; | |
} | |
// セクションの本体 | |
if (nextType === "SECTION_BODY" || nextType === "SECTION_BODY_SUB") { | |
// 改行のみの場合、サブセクションの可能性がある | |
if (line.trim() === "") { | |
this.debug(">> This is empty line, next type is SECTION_BODY_SUB"); | |
nextType = "SECTION_BODY_SUB"; | |
continue; | |
} | |
// SECTION_BODY_SUBの状態でデータが始まる場合は、セクション内に新しいサブセクションを用意する | |
if (nextType === "SECTION_BODY_SUB") { | |
// サブセクションのセクション名を取得 | |
this.debug(">> This is sub section name, next type is SECTION_BODY"); | |
sections[currentSectionName].push({}); | |
nextType = "SECTION_BODY"; | |
} | |
const subSection = nowSection[nowSection.length - 1]; | |
// 前回のインデントサイズと同じであれば、前回のアイテムデータに追加 | |
const currentIndentSize = line.match(/^\s*/)[0].length; | |
if (currentIndentSize === indentSize) { | |
this.debug(">> This is same indent size, add to last item"); | |
subSection[lastKey] += "\n" + line.trim(); | |
continue; | |
} | |
// インデントサイズが変わった場合は、前回と異なるアイテムデータである | |
// アイテムデータのキーを取得 (例: " Card name: NVIDIA GeForce RTX 4070 Ti SUPER" の "Card name") | |
// 最初のスペース数、キー名、バリューを取得 | |
const match = line.match(/^(\s*)([^:]+):(.*)/); | |
if (!match) { | |
throw new Error(`Invalid line: ${line}`); | |
} | |
this.debug(">> This is new item"); | |
const spaceIndentSize = match[1].length; | |
const key = match[2].trim(); | |
const value = match[3].trim(); | |
// インデントサイズを更新 (スペース数 + キー名の長さ + 2文字 (": "の分) | |
indentSize = spaceIndentSize + key.length + 2; | |
// アイテムデータを作成 | |
// すでにキーがある場合はエラーとする | |
if (subSection[key]) { | |
throw new Error(`Duplicated key: ${key}`); | |
} | |
subSection[key] = value; | |
lastKey = key; | |
} | |
} | |
this.sections = sections; | |
} | |
/** | |
* マシン名を取得する | |
* | |
* @returns {string | null} マシン名 (取得できない場合は null) | |
*/ | |
getMachineName() { | |
if (!this.sections["System Information"][0]["Machine name"]) { | |
return null; | |
} | |
return this.sections["System Information"][0]["Machine name"]; | |
} | |
/** | |
* マシンIDを取得する | |
* | |
* @returns {string | null} マシンID (取得できない場合は null) | |
*/ | |
getMachineId() { | |
if (!this.sections["System Information"][0]["Machine Id"]) { | |
return null; | |
} | |
return this.sections["System Information"][0]["Machine Id"]; | |
} | |
/** | |
* OSを取得する | |
* | |
* @returns {string | null} OS (取得できない場合は null) | |
*/ | |
getOperatingSystem() { | |
if (!this.sections["System Information"][0]["Operating System"]) { | |
return null; | |
} | |
return this.sections["System Information"][0]["Operating System"]; | |
} | |
/** | |
* CPUを取得する | |
* | |
* @param {string} outputType 出力形式 (full: すべてを返す, brand: ブランドのみを返す, model: モデルのみを返す) | |
* @returns {string | null} CPU (取得できない場合は null) | |
*/ | |
getCenterProcessorUnit(outputType = "full") { | |
if (!this.sections["System Information"][0]["Processor"]) { | |
return null; | |
} | |
// スペースが途中に入ることがあるので、適宜減らす | |
const processor = this.sections["System Information"][0]["Processor"]; | |
const shortedProcessor = processor.replace(/ {2,}/g, " "); | |
if (outputType === "full") { | |
return shortedProcessor; | |
} | |
// Intel Core i9-9900KF @ 8x 8.3GHz | |
// Intel(R) Core(TM) i9-9900KF CPU @ 3.60GHz | |
// 13th Gen Intel(R) Core(TM) i9-13900KF (32 CPUs), ~3.0GHz | |
// 13th Gen Intel(R) Core(TM) i5-13400 | |
// AMD Ryzen 9 5900X 12-Core Processor (24 CPUs), ~3.7GHz | |
// AMD Ryzen 7 3700X 8-Core Processor (16 CPUs), ~3.6GHz | |
const regexs = [ | |
/^(?<brand>Intel) (?<model>.+) @ (?<extra>.+)$/i, | |
/^(?<brand>Intel)\(R\) (?<model>.+) CPU @ (?<extra>.+)$/i, | |
/^[0-9]+th Gen (?<brand>Intel)\(R\) (?<model>.+), (?<extra>.+)$/i, | |
/^[0-9]+th Gen (?<brand>Intel)\(R\) (?<model>.+)$/i, | |
/^(?<brand>AMD) (?<model>.+), (?<extra>.+)$/i, | |
]; | |
for (const regex of regexs) { | |
const match = shortedProcessor.match(regex); | |
if (match) { | |
if (outputType === "brand") { | |
return match.groups.brand; | |
} | |
if (outputType === "model") { | |
return match.groups.model.replace(/\(TM\)/g, ""); | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* メモリを取得する | |
* | |
* @returns {string | null} MB形式メモリ (取得できない場合は null) | |
*/ | |
getMemory() { | |
if (!this.sections["System Information"][0]["Memory"]) { | |
return null; | |
} | |
return this.sections["System Information"][0]["Memory"]; | |
} | |
/** | |
* メモリをバイト数で取得する | |
* | |
* @returns {number | null} メモリバイト数 (取得できない場合は null) | |
*/ | |
getMemoryBytes() { | |
const memory = this.getMemory(); | |
if (!memory) { | |
return null; | |
} | |
const match = memory.match(/(\d+)MB/); | |
if (!match) { | |
return null; | |
} | |
return parseInt(match[1]) * 1024 * 1024; | |
} | |
/** | |
* グラフィックカードを取得する | |
* | |
* @param {string} outputType 出力形式 (full: すべてを返す, brand: ブランドのみを返す, model: モデルのみを返す) | |
* @returns {string[] | null} グラフィックカード名の配列 (取得できない場合は null) | |
*/ | |
getGraphicsCard(outputType = "full") { | |
if (!this.sections["Display Devices"]) { | |
return null; | |
} | |
const displayDevices = this.sections["Display Devices"]; | |
if (displayDevices.length === 0) { | |
return null; | |
} | |
const cardNames = displayDevices | |
.map((device) => device["Card name"]) | |
.map((cardName) => { | |
const match = cardName.match(/^(.+?) (.+)$/); | |
if (outputType === "full") { | |
return cardName; | |
} | |
if (outputType === "brand") { | |
return match[1]; | |
} | |
if (outputType === "model") { | |
return match[2]; | |
} | |
return null; | |
}) | |
.filter((value) => value !== null) | |
.filter((value, index, self) => self.indexOf(value) === index); | |
return cardNames; | |
} | |
/** | |
* ディスクドライブのモデルを取得する | |
* | |
* @param {boolean} withDriveLetter ドライブレターを含めるかどうか (デフォルト: false) | |
* @returns {string[] | null} ディスクドライブのモデル名の配列 (取得できない場合は null) | |
*/ | |
getDiskDrives(withDriveLetter = false) { | |
if (!this.sections["Disk & DVD/CD-ROM Drives"]) { | |
return null; | |
} | |
const diskDrives = this.sections["Disk & DVD/CD-ROM Drives"]; | |
if (diskDrives.length === 0) { | |
return null; | |
} | |
const driveNames = diskDrives | |
// Driverがあり、かつCDROM.SYSがある場合は除外 | |
.filter( | |
(drive) => !drive["Driver"] || !drive["Driver"].includes("CDROM.SYS") | |
) | |
.map( | |
(drive) => | |
drive["Model"] + (withDriveLetter ? ` (${drive["Drive"]})` : "") | |
) | |
.filter((value, index, self) => self.indexOf(value) === index); | |
return driveNames; | |
} | |
/** | |
* デバッグメッセージを出力する | |
* | |
* @param {*} text デバッグメッセージ | |
*/ | |
debug(text) { | |
if (!this.isDebug) { | |
return; | |
} | |
console.log(text); | |
} | |
} | |
/** | |
* 指定した値を持つヘッダーの列番号を取得する関数 | |
* | |
* @param {string} sheet - 対象のシート | |
* @param {string[]} targetKeys - 検索するヘッダーの値群 | |
* @return {number} 指定した値を持つ列の番号(見つからない場合は-1を返す) | |
*/ | |
function getColumnNumbers(sheet, targetKeys) { | |
// シートが存在しない場合はエラーを返す | |
if (!sheet) { | |
throw new Error(`シートが見つかりませんでした`); | |
} | |
// 1行目(ヘッダー)の全ての値を取得 | |
const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; | |
const results = {}; | |
// 指定した値がヘッダー内のどの列にあるかを調べる | |
for (let index = 0; index < headers.length; index++) { | |
// 現在のヘッダーの値と対象値を比較、一致した場合はその列番号を返す | |
if (targetKeys.includes(headers[index])) { | |
results[headers[index]] = index + 1; | |
} | |
} | |
return results; | |
} | |
const fs = require("fs"); | |
const iconv = require("iconv-lite"); | |
function main() { | |
// shift_jis で読み込む | |
const dxdiagBuffer = fs.readFileSync("DxDiag.txt"); | |
const dxdiag = iconv.decode(dxdiagBuffer, "shift_jis"); | |
const parser = new DxdiagParser(dxdiag); | |
console.log("Machine Name:", parser.getMachineName()); | |
console.log("Machine Id:", parser.getMachineId()); | |
console.log("Operating System:", parser.getOperatingSystem()); | |
console.log("CPU:", parser.getCenterProcessorUnit()); | |
console.log("Memory:", parser.getMemory()); | |
console.log("Memory Bytes:", parser.getMemoryBytes()); | |
console.log("Graphics Card:", parser.getGraphicsCard()); | |
console.log("Disk Drives:", parser.getDiskDrives()); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment