# parse .ini file

[JS](https://lochiwei.gitbook.io/web/js) ⟩ [value](https://lochiwei.gitbook.io/web/js/val) ⟩ [object](https://lochiwei.gitbook.io/web/js/val/obj) ⟩ [regex](https://lochiwei.gitbook.io/web/js/val/builtin/regex) ⟩ [example](https://lochiwei.gitbook.io/web/js/val/builtin/regex/ex) ⟩ parse .ini file

{% hint style="success" %}

```javascript
// using "ini" package from npm
const { parse: parseINI } = require("ini");
parseINI("x = 10\ny = 20"),    // { x: '10', y: '20' }
```

{% endhint %}

{% tabs %}
{% tab title="💈範例" %}

* replit ⟩ [parse .ini file](https://replit.com/@pegasusroe/parse-ini-file-1#index.js),  [INIParser](https://replit.com/@pegasusroe/INIParser#INIParser.js) (old code)

```javascript
// ⭐ .ini file format
// ---------------------------
// 1. Blank lines and lines starting with semicolons are ignored.
// 2. Lines wrapped in [ and ] start a new section.
// 3. Lines containing an alphanumeric identifier followed by an = character 
//    add a setting to the current section.
// 4. Anything else is invalid.
function parseINI(string) {

    let result = {};                // object to hold the top-level fields
    let currentSection = result;    // current section

    // ----------------------------
    // patterns (without '/g' flag)
    // ----------------------------
    
    const setting = /^(?<key>\w+)=(?<value>.*)$/;
    //                ╰── key ──╯ ╰─ value ──╯  <---- named groups

    const section = /^\[(?<sectionName>.*)\]$/;
    //                  ╰─ section name ─╯ 

    const blankOrComment = /^\s*(;.*)?$/;

    // begin to parse
    string
        .split(/\r?\n/)                  // split into separate lines
        .forEach(line => {
            
            let match;

            // ignore blank line or comment
            if (blankOrComment.test(line)) return;
            
            // new setting to current section
            if (match = line.match(setting)) {
                const { key, value } = match.groups;    // named groups
                currentSection[key] = value;
                return;                  // return from closure (early exit)
            }
            
            // new section
            if (match = line.match(section)) {
                const { sectionName } = match.groups;    // named groups
                currentSection = result[sectionName] = {};
                return;
            } 
            
            // anything else is invalid.
            throw new Error("Line: '" + line + "' is not valid.");
        });

    return result;
}
```

💈範例：&#x20;

```javascript
// ⭐ example of an .ini file
const ini = `
; comments are preceded by a semicolon...
; [sectionName] starts a new section.
; 'key=value' adds a new setting to current section.

game=God Of War

[larry]
fullname=Larry Doe
type=kindergarten bully

[davaeorn]
fullname=Davaeorn
type=evil wizard`;

// test run
parseINI(ini)        // string -> object
// {
//   game: 'God Of War',
//   larry: { fullname: 'Larry Doe', type: 'kindergarten bully' },
//   davaeorn: { fullname: 'Davaeorn', type: 'evil wizard' }
// }
```

{% endtab %}

{% tab title="📗 參考" %}

* [ ] Eloquent JS ⟩ Regular Expressions ⟩ [Parsing an .ini File](https://eloquentjavascript.net/09_regexp.html#h_RGsf6ah1EY)
  {% endtab %}

{% tab title="🛠 工具" %}

* npm ⟩ package: [ini](https://www.npmjs.com/package/ini)
  {% endtab %}

{% tab title="📘 手冊" %}

* [str.match(regex)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match)
  {% endtab %}
  {% endtabs %}
