← Back to Guides

CSV & INI Parsing Guide

GoPLC CSV & INI Parsing Guide

James M. Belcher Founder, JMB Technical Services LLC April 2026 | GoPLC v1.0.535


1. Overview

GoPLC provides 20 built-in functions for working with two common data file formats from Structured Text:

FormatFunctionsUse Case
CSV10Data import/export, recipes, tag lists, report generation
INI10Configuration files, device settings, recipe parameters

Both support in-memory parsing (string → handle → query/modify → string) and CSV/INI files can be combined with the FILE_* functions for disk operations.


2. CSV Functions

Parse CSV Data

(* Parse a CSV string — first row treated as headers *)
data := CSV_PARSE('name,temp,pressure
Pump-1,72.5,45.3
Pump-2,68.1,42.0
Pump-3,71.8,44.7');

rows := CSV_ROW_COUNT(data);          (* 4 — includes header row *)
cols := CSV_COL_COUNT(data);          (* 3 *)
ParamTypeDescription
textSTRINGCSV text to parse
delimiterSTRINGOptional — custom delimiter (default: comma)

Returns: handle for use with other CSV_* functions.

Read Fields

(* By row/column index — 0-based *)
name := CSV_GET_FIELD(data, 1, 0);    (* "Pump-1" — row 1, col 0 *)
temp := CSV_GET_FIELD(data, 1, 1);    (* "72.5" *)

(* Get entire row as array *)
row := CSV_GET_ROW(data, 2);          (* ["Pump-2", "68.1", "42.0"] *)

(* Get headers *)
headers := CSV_GET_HEADER(data);      (* ["name", "temp", "pressure"] *)

(* Find column index by header name *)
temp_col := CSV_FIND_COL(data, 'temp');    (* 1 *)
press_col := CSV_FIND_COL(data, 'pressure'); (* 2 *)

(* Name-based access: find column, then read *)
temp_val := CSV_GET_FIELD(data, 1, CSV_FIND_COL(data, 'temp'));

Modify CSV

(* Update a field *)
data := CSV_SET_FIELD(data, 1, 1, '75.0');

(* Add a new row *)
data := CSV_ADD_ROW(data, 'Pump-4', '69.5', '41.2');

Export to String

csv_text := CSV_TO_STRING(data);

(* With custom delimiter *)
tsv_text := CSV_TO_STRING(data, CHR(9));    (* Tab-separated *)

Parse with Custom Delimiter

(* Tab-separated *)
data := CSV_PARSE(tsv_content, CHR(9));

(* Semicolon-separated (European format) *)
data := CSV_PARSE(euro_content, ';');

Example: Load Recipe from CSV File

PROGRAM POU_CSVRecipe
VAR
    state : INT := 0;
    csv_text : STRING;
    data : STRING;
    recipe_count : INT;
    i : INT;
    name : STRING;
    temp : REAL;
    pressure : REAL;
    time_sec : INT;
    name_col : INT;
    temp_col : INT;
    press_col : INT;
    time_col : INT;
END_VAR

CASE state OF
    0: (* Read CSV file *)
        IF FILE_EXISTS('/data/recipes.csv') THEN
            csv_text := FILE_READ('/data/recipes.csv');
            data := CSV_PARSE(csv_text);

            (* Find columns by header name *)
            name_col := CSV_FIND_COL(data, 'name');
            temp_col := CSV_FIND_COL(data, 'temperature');
            press_col := CSV_FIND_COL(data, 'pressure');
            time_col := CSV_FIND_COL(data, 'time');

            recipe_count := CSV_ROW_COUNT(data) - 1;    (* Exclude header *)
            state := 10;
        END_IF;

    10: (* Process recipes — skip row 0 (headers) *)
        FOR i := 1 TO recipe_count DO
            name := CSV_GET_FIELD(data, i, name_col);
            temp := STRING_TO_REAL(CSV_GET_FIELD(data, i, temp_col));
            pressure := STRING_TO_REAL(CSV_GET_FIELD(data, i, press_col));
            time_sec := STRING_TO_INT(CSV_GET_FIELD(data, i, time_col));
            (* Use recipe values... *)
        END_FOR;
END_CASE;
END_PROGRAM

Example: Generate CSV Report

PROGRAM POU_CSVReport
VAR
    data : STRING;
    initialized : BOOL := FALSE;
    scan_count : DINT := 0;
    temperature : REAL;
    pressure : REAL;
END_VAR

IF NOT initialized THEN
    (* Create CSV with headers *)
    data := CSV_PARSE('timestamp,temperature,pressure');
    initialized := TRUE;
END_IF;

scan_count := scan_count + 1;

(* Add a row every 100 scans *)
IF (scan_count MOD 100) = 0 THEN
    data := CSV_ADD_ROW(data,
        DT_TO_STRING(NOW()),
        REAL_TO_STRING(temperature),
        REAL_TO_STRING(pressure)
    );

    (* Save to file every 1000 scans *)
    IF (scan_count MOD 1000) = 0 THEN
        FILE_WRITE('/data/report.csv', CSV_TO_STRING(data));
    END_IF;
END_IF;
END_PROGRAM

3. INI Functions

INI files use a [section] + key=value format common in device configuration.

Two Access Patterns

PatternFunctionsDescription
File I/OINI_READ, INI_WRITERead/write directly to disk
In-MemoryINI_PARSE → query/modify → INI_TO_STRINGParse string, manipulate, serialize

Direct File Access

(* Read a value from an INI file *)
host := INI_READ('/data/config.ini', 'modbus', 'host', '10.0.0.50');
port := INI_READ('/data/config.ini', 'modbus', 'port', '502');

(* Write a value to an INI file *)
INI_WRITE('/data/config.ini', 'modbus', 'host', '10.0.0.51');
INI_WRITE('/data/config.ini', 'runtime', 'scan_ms', '50');
ParamTypeDescription
filepathSTRINGPath to .ini file
sectionSTRINGSection name (without brackets)
keySTRINGKey name
defaultSTRINGINI_READ only — returned if key not found

INI_WRITE creates the file, section, and key if they don’t exist.

In-Memory Parsing

(* Parse INI text *)
cfg := INI_PARSE('
[modbus]
host=10.0.0.50
port=502
unit_id=1

[mqtt]
broker=tcp://10.0.0.144:1883
topic_prefix=plant/data
');

(* Read values *)
host := INI_GET(cfg, 'modbus', 'host', '');           (* "10.0.0.50" *)
port := INI_GET(cfg, 'modbus', 'port', '502');        (* "502" *)
broker := INI_GET(cfg, 'mqtt', 'broker', '');          (* "tcp://10.0.0.144:1883" *)

(* List sections and keys *)
sections := INI_SECTIONS(cfg);                         (* ["modbus", "mqtt"] *)
keys := INI_KEYS(cfg, 'modbus');                       (* ["host", "port", "unit_id"] *)

Modify In-Memory

(* Set/update values *)
cfg := INI_SET(cfg, 'modbus', 'host', '10.0.0.51');
cfg := INI_SET(cfg, 'modbus', 'timeout_ms', '5000');   (* Adds new key *)
cfg := INI_SET(cfg, 'logging', 'level', 'info');        (* Creates new section *)

(* Delete *)
cfg := INI_DELETE_KEY(cfg, 'modbus', 'timeout_ms');
cfg := INI_DELETE_SECTION(cfg, 'logging');

(* Serialize back to string *)
ini_text := INI_TO_STRING(cfg);

(* Save to file *)
FILE_WRITE('/data/config.ini', ini_text);

Example: Device Configuration Manager

PROGRAM POU_DeviceConfig
VAR
    state : INT := 0;
    cfg : STRING;
    ini_text : STRING;

    (* Loaded config *)
    mb_host : STRING;
    mb_port : INT;
    mb_unit : INT;
    scan_ms : INT;
    log_level : STRING;
END_VAR

CASE state OF
    0: (* Load config file *)
        IF FILE_EXISTS('/data/device.ini') THEN
            ini_text := FILE_READ('/data/device.ini');
            cfg := INI_PARSE(ini_text);
        ELSE
            (* Create default config *)
            cfg := INI_PARSE('');
            cfg := INI_SET(cfg, 'modbus', 'host', '10.0.0.50');
            cfg := INI_SET(cfg, 'modbus', 'port', '502');
            cfg := INI_SET(cfg, 'modbus', 'unit_id', '1');
            cfg := INI_SET(cfg, 'runtime', 'scan_ms', '50');
            cfg := INI_SET(cfg, 'runtime', 'log_level', 'info');
            FILE_WRITE('/data/device.ini', INI_TO_STRING(cfg));
        END_IF;

        (* Apply config *)
        mb_host := INI_GET(cfg, 'modbus', 'host', '10.0.0.50');
        mb_port := STRING_TO_INT(INI_GET(cfg, 'modbus', 'port', '502'));
        mb_unit := STRING_TO_INT(INI_GET(cfg, 'modbus', 'unit_id', '1'));
        scan_ms := STRING_TO_INT(INI_GET(cfg, 'runtime', 'scan_ms', '50'));
        log_level := INI_GET(cfg, 'runtime', 'log_level', 'info');
        state := 10;

    10: (* Running — config applied *)
END_CASE;
END_PROGRAM

Appendix A: Quick Reference

CSV Functions (10)

FunctionParametersReturnsDescription
CSV_PARSE(text [, delim])1-2HandleParse CSV string
CSV_GET_FIELD(h, row, col)3STRINGRead field by index
CSV_GET_ROW(h, row)2ARRAYGet entire row
CSV_GET_HEADER(h)1ARRAYGet first row (headers)
CSV_FIND_COL(h, name)2INTFind column by header name (-1 if missing)
CSV_ROW_COUNT(h)1INTNumber of rows (including header)
CSV_COL_COUNT(h [, row])1-2INTNumber of columns
CSV_SET_FIELD(h, row, col, val)4HandleUpdate field
CSV_ADD_ROW(h, val1, val2, ...)2+HandleAppend row
CSV_TO_STRING(h [, delim])1-2STRINGSerialize to CSV text

INI Functions (10)

FunctionParametersReturnsDescription
INI_READ(path, section, key [, default])3-4STRINGRead from file
INI_WRITE(path, section, key, value)4BOOLWrite to file
INI_PARSE(text)1HandleParse INI string
INI_GET(h, section, key [, default])3-4STRINGRead from handle
INI_SET(h, section, key, value)4HandleSet value in handle
INI_SECTIONS(h)1ARRAYList section names
INI_KEYS(h, section)2ARRAYList keys in section
INI_TO_STRING(h)1STRINGSerialize to INI text
INI_DELETE_KEY(h, section, key)3HandleRemove key
INI_DELETE_SECTION(h, section)2HandleRemove section

GoPLC v1.0.535 | 20 CSV & INI Functions | Data Import/Export & Configuration

© 2026 JMB Technical Services LLC. All rights reserved.