Task 697: .STP File Format

Task 697: .STP File Format

.STP File Format Specifications

The .STP file format refers to the STEP (Standard for the Exchange of Product Model Data) format, defined by ISO 10303-21. It is a text-based (ASCII or UTF-8) format for representing and exchanging 3D product data, commonly used in CAD, engineering, and manufacturing. The file structure is sequential, starting with the magic string ISO-10303-21;, followed by a mandatory HEADER section, optional ANCHOR and REFERENCE sections, one or more DATA sections containing entity instances, and ending with END-ISO-10303-21;. Optional SIGNATURE sections can follow. Sections are delimited by ENDSEC;. The format supports conformance classes (1-3) for features like external references and signatures. Data is encoded using EXPRESS schema mappings, with entities like #ID=ENTITY_NAME(attributes);.

  1. List of all the properties of this file format intrinsic to its file system:
  • Description: A list of strings describing the file's content (from FILE_DESCRIPTION).
  • Implementation Level: A string indicating the ISO 10303-21 version and conformance class (e.g., '4;1') (from FILE_DESCRIPTION).
  • Name: The file's name or identifier (from FILE_NAME).
  • Time Stamp: An ISO 8601-formatted date-time string of creation (from FILE_NAME).
  • Author: A list of strings with author names/addresses (from FILE_NAME).
  • Organization: A list of strings with organization names (from FILE_NAME).
  • Preprocessor Version: A string with the tool/version used to create the file (from FILE_NAME).
  • Originating System: A string identifying the source system (from FILE_NAME).
  • Authorization: A string with the authorizer's name/address (from FILE_NAME).
  • Schema Identifiers: A list of schema names (e.g., 'CONFIG_CONTROL_DESIGN') (from FILE_SCHEMA).
  • Schema Population (optional): A list of external file identifications (URIs, timestamps, digests) for referenced files (from SCHEMA_POPULATION).
  • File Population (optional): Details on governing schema, determination method, and governed sections (from FILE_POPULATION).
  • Default Language (optional): A language code (e.g., 'eng') for strings in data sections (from SECTION_LANGUAGE).
  • Context Identifiers (optional): A list of strings for contextual tags (e.g., conformance classes) (from SECTION_CONTEXT).

These properties are primarily in the HEADER section and are intrinsic as they define the file's metadata, schema conformance, and exchange context.

Two direct download links for files of format .STP:

Ghost blog embedded HTML JavaScript for drag-and-drop .STP file to dump properties to screen:

Drag and drop a .STP file here

This can be embedded in a Ghost blog post. It allows drag-and-drop of a .STP file, parses the HEADER, and displays the properties as JSON. Note: This is a basic parser; for production, use a robust library like stepcode to handle escapes, complex structures, and errors.

  1. Python class for .STP handling:
import re
import json

class StpHandler:
    def __init__(self, file_path):
        self.file_path = file_path
        self.properties = {}
        self.content = ''
        self._read_and_decode()

    def _read_and_decode(self):
        with open(self.file_path, 'r', encoding='utf-8') as f:
            self.content = f.read()
        lines = self.content.split('\n')
        in_header = False
        header_content = ''
        for line in lines:
            line = line.strip()
            if line.startswith('HEADER;'):
                in_header = True
                continue
            if line.startswith('ENDSEC;') and in_header:
                in_header = False
                break
            if in_header:
                header_content += line

        self.properties = {
            'description': [],
            'implementation_level': '',
            'name': '',
            'time_stamp': '',
            'author': [],
            'organization': [],
            'preprocessor_version': '',
            'originating_system': '',
            'authorization': '',
            'schema_identifiers': [],
            'schema_population': [],
            'file_population': {'governing_schema': '', 'determination_method': '', 'governed_sections': []},
            'default_language': '',
            'context_identifiers': []
        }

        # Basic regex-based parsing (assumes no deep nesting/escapes)
        entities = re.split(r';(?![^(]*\))', header_content)
        for entity in entities:
            entity = entity.strip()
            if not entity:
                continue
            if 'FILE_DESCRIPTION' in entity:
                params = re.search(r'\((.*)\)', entity).group(1).split(',(?![^(]*\))', maxsplit=1)
                self.properties['description'] = self._parse_list(params[0])
                self.properties['implementation_level'] = params[1].strip("'") if len(params) > 1 else ''
            elif 'FILE_NAME' in entity:
                params = re.search(r'\((.*)\)', entity).group(1).split(',(?![^(]*\))', maxsplit=6)
                self.properties['name'] = params[0].strip("'")
                self.properties['time_stamp'] = params[1].strip("'")
                self.properties['author'] = self._parse_list(params[2])
                self.properties['organization'] = self._parse_list(params[3])
                self.properties['preprocessor_version'] = params[4].strip("'")
                self.properties['originating_system'] = params[5].strip("'")
                self.properties['authorization'] = params[6].strip("'") if len(params) > 6 else ''
            elif 'FILE_SCHEMA' in entity:
                params = re.search(r'\((.*)\)', entity).group(1)
                self.properties['schema_identifiers'] = self._parse_list(params)
            elif 'SCHEMA_POPULATION' in entity:
                params = re.search(r'\((.*)\)', entity).group(1)
                self.properties['schema_population'] = self._parse_list(params)
            elif 'FILE_POPULATION' in entity:
                params = re.search(r'\((.*)\)', entity).group(1).split(',(?![^(]*\))', maxsplit=2)
                self.properties['file_population']['governing_schema'] = params[0].strip("'")
                self.properties['file_population']['determination_method'] = params[1].strip("'")
                self.properties['file_population']['governed_sections'] = self._parse_list(params[2]) if len(params) > 2 else []
            elif 'SECTION_LANGUAGE' in entity:
                params = re.search(r'\((.*)\)', entity).group(1).split(',')
                self.properties['default_language'] = params[1].strip("'") if len(params) > 1 else ''
            elif 'SECTION_CONTEXT' in entity:
                params = re.search(r'\((.*)\)', entity).group(1).split(',(?![^(]*\))', maxsplit=1)
                self.properties['context_identifiers'] = self._parse_list(params[1]) if len(params) > 1 else []

    def _parse_list(self, s):
        s = s.strip('()')
        if not s:
            return []
        items = re.split(r',(?!(?:[^()]*\([^()]*\))*[^()]*$)', s)
        return [item.strip("'").strip() for item in items if item.strip()]

    def print_properties(self):
        print(json.dumps(self.properties, indent=2))

    def write(self, new_file_path, modified_properties=None):
        if modified_properties:
            self.properties.update(modified_properties)
        # Rebuild header (basic; preserves data sections)
        new_header = 'HEADER;\n'
        new_header += f"FILE_DESCRIPTION({self._format_list(self.properties['description'])},'{self.properties['implementation_level']}');\n"
        new_header += f"FILE_NAME('{self.properties['name']}','{self.properties['time_stamp']}',{self._format_list(self.properties['author'])},{self._format_list(self.properties['organization'])},'{self.properties['preprocessor_version']}','{self.properties['originating_system']}','{self.properties['authorization']}');\n"
        new_header += f"FILE_SCHEMA({self._format_list(self.properties['schema_identifiers'])});\n"
        if self.properties['schema_population']:
            new_header += f"SCHEMA_POPULATION({self._format_list(self.properties['schema_population'])});\n"
        if self.properties['file_population']['governing_schema']:
            fp = self.properties['file_population']
            new_header += f"FILE_POPULATION('{fp['governing_schema']}','{fp['determination_method']}',{self._format_list(fp['governed_sections'])});\n"
        if self.properties['default_language']:
            new_header += f"SECTION_LANGUAGE($,'{self.properties['default_language']}');\n"
        if self.properties['context_identifiers']:
            new_header += f"SECTION_CONTEXT($,{self._format_list(self.properties['context_identifiers'])});\n"
        new_header += 'ENDSEC;\n'

        # Replace original header in content
        content_without_header = re.sub(r'HEADER;.*?ENDSEC;', new_header, self.content, flags=re.DOTALL)
        with open(new_file_path, 'w', encoding='utf-8') as f:
            f.write(content_without_header)

    def _format_list(self, lst):
        if not lst:
            return '()'
        return '(' + ','.join(f"'{item}'" if isinstance(item, str) else str(item) for item in lst) + ')'

# Example usage:
# handler = StpHandler('example.stp')
# handler.print_properties()
# handler.write('modified.stp', {'name': 'new_name'})
  1. Java class for .STP handling:
import java.io.*;
import java.util.*;
import java.util.regex.*;

public class StpHandler {
    private String filePath;
    private Map<String, Object> properties;
    private String content;

    public StpHandler(String filePath) {
        this.filePath = filePath;
        this.properties = new HashMap<>();
        this.content = "";
        readAndDecode();
    }

    private void readAndDecode() {
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            content = new String(Files.readAllBytes(new File(filePath).toPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }

        Pattern headerPattern = Pattern.compile("HEADER;(.*?)ENDSEC;", Pattern.DOTALL);
        Matcher matcher = headerPattern.matcher(content);
        String headerContent = "";
        if (matcher.find()) {
            headerContent = matcher.group(1);
        }

        properties.put("description", new ArrayList<String>());
        properties.put("implementation_level", "");
        properties.put("name", "");
        properties.put("time_stamp", "");
        properties.put("author", new ArrayList<String>());
        properties.put("organization", new ArrayList<String>());
        properties.put("preprocessor_version", "");
        properties.put("originating_system", "");
        properties.put("authorization", "");
        properties.put("schema_identifiers", new ArrayList<String>());
        properties.put("schema_population", new ArrayList<String>());
        Map<String, Object> filePopulation = new HashMap<>();
        filePopulation.put("governing_schema", "");
        filePopulation.put("determination_method", "");
        filePopulation.put("governed_sections", new ArrayList<String>());
        properties.put("file_population", filePopulation);
        properties.put("default_language", "");
        properties.put("context_identifiers", new ArrayList<String>());

        String[] entities = headerContent.split(";");
        for (String entity : entities) {
            entity = entity.trim();
            if (entity.isEmpty()) continue;
            if (entity.contains("FILE_DESCRIPTION")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                String[] split = params.split(",(?![^(]*\\))", 2);
                ((List<String>) properties.get("description")).addAll(parseList(split[0]));
                properties.put("implementation_level", split[1].replace("'", "").trim());
            } else if (entity.contains("FILE_NAME")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                String[] split = params.split(",(?![^(]*\\))", 7);
                properties.put("name", split[0].replace("'", "").trim());
                properties.put("time_stamp", split[1].replace("'", "").trim());
                ((List<String>) properties.get("author")).addAll(parseList(split[2]));
                ((List<String>) properties.get("organization")).addAll(parseList(split[3]));
                properties.put("preprocessor_version", split[4].replace("'", "").trim());
                properties.put("originating_system", split[5].replace("'", "").trim());
                properties.put("authorization", split.length > 6 ? split[6].replace("'", "").trim() : "");
            } else if (entity.contains("FILE_SCHEMA")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                ((List<String>) properties.get("schema_identifiers")).addAll(parseList(params));
            } else if (entity.contains("SCHEMA_POPULATION")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                ((List<String>) properties.get("schema_population")).addAll(parseList(params));
            } else if (entity.contains("FILE_POPULATION")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                String[] split = params.split(",(?![^(]*\\))", 3);
                ((Map<String, Object>) properties.get("file_population")).put("governing_schema", split[0].replace("'", "").trim());
                ((Map<String, Object>) properties.get("file_population")).put("determination_method", split[1].replace("'", "").trim());
                if (split.length > 2) {
                    ((Map<String, Object>) properties.get("file_population")).put("governed_sections", parseList(split[2]));
                }
            } else if (entity.contains("SECTION_LANGUAGE")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                String[] split = params.split(",");
                properties.put("default_language", split.length > 1 ? split[1].replace("'", "").trim() : "");
            } else if (entity.contains("SECTION_CONTEXT")) {
                String params = entity.substring(entity.indexOf("(") + 1, entity.lastIndexOf(")"));
                String[] split = params.split(",(?![^(]*\\))", 2);
                if (split.length > 1) {
                    ((List<String>) properties.get("context_identifiers")).addAll(parseList(split[1]));
                }
            }
        }
    }

    private List<String> parseList(String s) {
        s = s.trim().replaceAll("^\\(|\\)$", "");
        if (s.isEmpty()) return new ArrayList<>();
        String[] items = s.split(",(?!(?:[^()]*\\([^()]*\\))*[^()]*$)");
        List<String> list = new ArrayList<>();
        for (String item : items) {
            list.add(item.replace("'", "").trim());
        }
        return list;
    }

    public void printProperties() {
        System.out.println(properties);
    }

    public void write(String newFilePath, Map<String, Object> modifiedProperties) {
        if (modifiedProperties != null) {
            for (Map.Entry<String, Object> entry : modifiedProperties.entrySet()) {
                if (entry.getKey().equals("file_population") && entry.getValue() instanceof Map) {
                    ((Map<String, Object>) properties.get("file_population")).putAll((Map<String, Object>) entry.getValue());
                } else {
                    properties.put(entry.getKey(), entry.getValue());
                }
            }
        }
        // Rebuild header
        StringBuilder newHeader = new StringBuilder("HEADER;\n");
        newHeader.append("FILE_DESCRIPTION(").append(formatList((List<String>) properties.get("description"))).append(",'").append(properties.get("implementation_level")).append("');\n");
        newHeader.append("FILE_NAME('").append(properties.get("name")).append("','").append(properties.get("time_stamp")).append("',")
                .append(formatList((List<String>) properties.get("author"))).append(",")
                .append(formatList((List<String>) properties.get("organization"))).append(",'")
                .append(properties.get("preprocessor_version")).append("','").append(properties.get("originating_system")).append("','")
                .append(properties.get("authorization")).append("');\n");
        newHeader.append("FILE_SCHEMA(").append(formatList((List<String>) properties.get("schema_identifiers"))).append(");\n");
        List<String> schemaPop = (List<String>) properties.get("schema_population");
        if (!schemaPop.isEmpty()) {
            newHeader.append("SCHEMA_POPULATION(").append(formatList(schemaPop)).append(");\n");
        }
        Map<String, Object> fp = (Map<String, Object>) properties.get("file_population");
        if (!fp.get("governing_schema").equals("")) {
            newHeader.append("FILE_POPULATION('").append(fp.get("governing_schema")).append("','").append(fp.get("determination_method")).append("',")
                    .append(formatList((List<String>) fp.get("governed_sections"))).append(");\n");
        }
        if (!properties.get("default_language").equals("")) {
            newHeader.append("SECTION_LANGUAGE($,'").append(properties.get("default_language")).append("');\n");
        }
        List<String> contextIds = (List<String>) properties.get("context_identifiers");
        if (!contextIds.isEmpty()) {
            newHeader.append("SECTION_CONTEXT($,").append(formatList(contextIds)).append(");\n");
        }
        newHeader.append("ENDSEC;\n");

        // Replace in content
        String newContent = content.replaceAll("HEADER;.*?ENDSEC;", newHeader.toString());
        try (FileWriter writer = new FileWriter(newFilePath)) {
            writer.write(newContent);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String formatList(List<String> list) {
        if (list.isEmpty()) return "()";
        StringBuilder sb = new StringBuilder("(");
        for (String item : list) {
            sb.append("'").append(item).append("',");
        }
        return sb.substring(0, sb.length() - 1) + ")";
    }

    // Example usage:
    // public static void main(String[] args) {
    //     StpHandler handler = new StpHandler("example.stp");
    //     handler.printProperties();
    //     handler.write("modified.stp", null);
    // }
}
  1. JavaScript class for .STP handling (Node.js compatible; for browser, adapt with FileReader):
const fs = require('fs');

class StpHandler {
  constructor(filePath) {
    this.filePath = filePath;
    this.properties = {};
    this.content = '';
    this.readAndDecode();
  }

  readAndDecode() {
    this.content = fs.readFileSync(this.filePath, 'utf-8');
    const lines = this.content.split('\n');
    let inHeader = false;
    let headerContent = '';
    for (let line of lines) {
      line = line.trim();
      if (line.startsWith('HEADER;')) {
        inHeader = true;
        continue;
      }
      if (line.startsWith('ENDSEC;') && inHeader) {
        inHeader = false;
        break;
      }
      if (inHeader) {
        headerContent += line;
      }
    }

    this.properties = {
      description: [],
      implementation_level: '',
      name: '',
      time_stamp: '',
      author: [],
      organization: [],
      preprocessor_version: '',
      originating_system: '',
      authorization: '',
      schema_identifiers: [],
      schema_population: [],
      file_population: { governing_schema: '', determination_method: '', governed_sections: [] },
      default_language: '',
      context_identifiers: []
    };

    const entities = headerContent.split(';').filter(e => e.trim());
    entities.forEach(entity => {
      if (entity.includes('FILE_DESCRIPTION')) {
        const params = entity.match(/\((.*)\)/)[1].split(/,(?![^(]*\))/);
        this.properties.description = this.parseList(params[0]);
        this.properties.implementation_level = params[1].replace(/'/g, '');
      } else if (entity.includes('FILE_NAME')) {
        const params = entity.match(/\((.*)\)/)[1].split(/,(?![^(]*\))/);
        this.properties.name = params[0].replace(/'/g, '');
        this.properties.time_stamp = params[1].replace(/'/g, '');
        this.properties.author = this.parseList(params[2]);
        this.properties.organization = this.parseList(params[3]);
        this.properties.preprocessor_version = params[4].replace(/'/g, '');
        this.properties.originating_system = params[5].replace(/'/g, '');
        this.properties.authorization = params[6].replace(/'/g, '');
      } else if (entity.includes('FILE_SCHEMA')) {
        const params = entity.match(/\((.*)\)/)[1];
        this.properties.schema_identifiers = this.parseList(params);
      } else if (entity.includes('SCHEMA_POPULATION')) {
        const params = entity.match(/\((.*)\)/)[1];
        this.properties.schema_population = this.parseList(params);
      } else if (entity.includes('FILE_POPULATION')) {
        const params = entity.match(/\((.*)\)/)[1].split(/,(?![^(]*\))/);
        this.properties.file_population.governing_schema = params[0].replace(/'/g, '');
        this.properties.file_population.determination_method = params[1].replace(/'/g, '');
        this.properties.file_population.governed_sections = this.parseList(params[2] || '()');
      } else if (entity.includes('SECTION_LANGUAGE')) {
        const params = entity.match(/\((.*)\)/)[1].split(',');
        this.properties.default_language = (params[1] || '').replace(/'/g, '');
      } else if (entity.includes('SECTION_CONTEXT')) {
        const params = entity.match(/\((.*)\)/)[1].split(/,(?![^(]*\))/);
        this.properties.context_identifiers = this.parseList(params[1] || '()');
      }
    });
  }

  parseList(s) {
    s = s.trim().replace(/^\(|\)$/g, '');
    if (!s) return [];
    const items = s.split(/,(?!(?:[^()]*\([^()]*\))*[^()]*$)/);
    return items.map(item => item.replace(/'/g, '').trim());
  }

  printProperties() {
    console.log(JSON.stringify(this.properties, null, 2));
  }

  write(newFilePath, modifiedProperties) {
    if (modifiedProperties) {
      Object.assign(this.properties, modifiedProperties);
      if (modifiedProperties.file_population) {
        Object.assign(this.properties.file_population, modifiedProperties.file_population);
      }
    }
    let newHeader = 'HEADER;\n';
    newHeader += `FILE_DESCRIPTION(${this.formatList(this.properties.description)},'${this.properties.implementation_level}');\n`;
    newHeader += `FILE_NAME('${this.properties.name}','${this.properties.time_stamp}',${this.formatList(this.properties.author)},${this.formatList(this.properties.organization)},'${this.properties.preprocessor_version}','${this.properties.originating_system}','${this.properties.authorization}');\n`;
    newHeader += `FILE_SCHEMA(${this.formatList(this.properties.schema_identifiers)});\n`;
    if (this.properties.schema_population.length) {
      newHeader += `SCHEMA_POPULATION(${this.formatList(this.properties.schema_population)});\n`;
    }
    const fp = this.properties.file_population;
    if (fp.governing_schema) {
      newHeader += `FILE_POPULATION('${fp.governing_schema}','${fp.determination_method}',${this.formatList(fp.governed_sections)});\n`;
    }
    if (this.properties.default_language) {
      newHeader += `SECTION_LANGUAGE($,'${this.properties.default_language}');\n`;
    }
    if (this.properties.context_identifiers.length) {
      newHeader += `SECTION_CONTEXT($,${this.formatList(this.properties.context_identifiers)});\n`;
    }
    newHeader += 'ENDSEC;\n';

    const newContent = this.content.replace(/HEADER;.*?ENDSEC;/s, newHeader);
    fs.writeFileSync(newFilePath, newContent, 'utf-8');
  }

  formatList(lst) {
    if (!lst.length) return '()';
    return '(' + lst.map(item => `'${item}'`).join(',') + ')';
  }
}

// Example usage:
// const handler = new StpHandler('example.stp');
// handler.printProperties();
// handler.write('modified.stp', { name: 'new_name' });
  1. C class (using C++ for class support):
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <regex>
#include <string>

class StpHandler {
private:
    std::string filePath;
    std::map<std::string, std::string> simpleProperties;
    std::map<std::string, std::vector<std::string>> listProperties;
    std::map<std::string, std::string> filePopulation;
    std::string content;

    std::vector<std::string> parseList(const std::string& s) {
        std::string cleaned = s;
        cleaned.erase(std::remove(cleaned.begin(), cleaned.end(), '('), cleaned.end());
        cleaned.erase(std::remove(cleaned.begin(), cleaned.end(), ')'), cleaned.end());
        if (cleaned.empty()) return {};
        std::vector<std::string> list;
        std::regex itemRegex(R"(([^,]+(?!(?:[^()]*\([^()]*\))*[^()]*$)))");
        std::sregex_iterator iter(cleaned.begin(), cleaned.end(), itemRegex);
        std::sregex_iterator end;
        for (; iter != end; ++iter) {
            std::string item = iter->str();
            item.erase(std::remove(item.begin(), item.end(), '\''), item.end());
            list.push_back(item);
        }
        return list;
    }

    std::string formatList(const std::vector<std::string>& lst) {
        if (lst.empty()) return "()";
        std::string res = "(";
        for (const auto& item : lst) {
            res += "'" + item + "',";
        }
        res.back() = ')';
        return res;
    }

public:
    StpHandler(const std::string& path) : filePath(path) {
        std::ifstream file(filePath);
        std::stringstream buffer;
        buffer << file.rdbuf();
        content = buffer.str();

        std::regex headerRegex(R"(HEADER;(.*?)ENDSEC;)", std::regex::dotall);
        std::smatch match;
        std::string headerContent;
        if (std::regex_search(content, match, headerRegex)) {
            headerContent = match[1].str();
        }

        listProperties["description"] = {};
        simpleProperties["implementation_level"] = "";
        simpleProperties["name"] = "";
        simpleProperties["time_stamp"] = "";
        listProperties["author"] = {};
        listProperties["organization"] = {};
        simpleProperties["preprocessor_version"] = "";
        simpleProperties["originating_system"] = "";
        simpleProperties["authorization"] = "";
        listProperties["schema_identifiers"] = {};
        listProperties["schema_population"] = {};
        filePopulation["governing_schema"] = "";
        filePopulation["determination_method"] = "";
        listProperties["governed_sections"] = {};
        simpleProperties["default_language"] = "";
        listProperties["context_identifiers"] = {};

        std::regex entityRegex(R"(([^;]+);)");
        std::sregex_iterator entityIter(headerContent.begin(), headerContent.end(), entityRegex);
        std::sregex_iterator entityEnd;
        for (; entityIter != entityEnd; ++entityIter) {
            std::string entity = entityIter->str(1);
            if (entity.find("FILE_DESCRIPTION") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    std::string params = paramsMatch[1].str();
                    size_t splitPos = params.find_last_of(',');
                    listProperties["description"] = parseList(params.substr(0, splitPos));
                    simpleProperties["implementation_level"] = params.substr(splitPos + 1);
                    simpleProperties["implementation_level"].erase(std::remove(simpleProperties["implementation_level"].begin(), simpleProperties["implementation_level"].end(), '\''), simpleProperties["implementation_level"].end());
                }
            } else if (entity.find("FILE_NAME") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    std::string params = paramsMatch[1].str();
                    std::vector<std::string> split; // Manual split for simplicity
                    std::stringstream ss(params);
                    std::string token;
                    while (std::getline(ss, token, ',')) {
                        if (token.find('(') != std::string::npos && token.find(')') == std::string::npos) {
                            std::string nested;
                            nested += token + ",";
                            while (std::getline(ss, token, ',')) {
                                nested += token + ",";
                                if (token.find(')') != std::string::npos) break;
                            }
                            split.push_back(nested.substr(0, nested.size() - 1));
                        } else {
                            split.push_back(token);
                        }
                    }
                    simpleProperties["name"] = split[0]; simpleProperties["name"].erase(std::remove(simpleProperties["name"].begin(), simpleProperties["name"].end(), '\''), simpleProperties["name"].end());
                    simpleProperties["time_stamp"] = split[1]; simpleProperties["time_stamp"].erase(std::remove(simpleProperties["time_stamp"].begin(), simpleProperties["time_stamp"].end(), '\''), simpleProperties["time_stamp"].end());
                    listProperties["author"] = parseList(split[2]);
                    listProperties["organization"] = parseList(split[3]);
                    simpleProperties["preprocessor_version"] = split[4]; simpleProperties["preprocessor_version"].erase(std::remove(simpleProperties["preprocessor_version"].begin(), simpleProperties["preprocessor_version"].end(), '\''), simpleProperties["preprocessor_version"].end());
                    simpleProperties["originating_system"] = split[5]; simpleProperties["originating_system"].erase(std::remove(simpleProperties["originating_system"].begin(), simpleProperties["originating_system"].end(), '\''), simpleProperties["originating_system"].end());
                    simpleProperties["authorization"] = split.size() > 6 ? split[6] : ""; simpleProperties["authorization"].erase(std::remove(simpleProperties["authorization"].begin(), simpleProperties["authorization"].end(), '\''), simpleProperties["authorization"].end());
                }
            } else if (entity.find("FILE_SCHEMA") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    listProperties["schema_identifiers"] = parseList(paramsMatch[1].str());
                }
            } else if (entity.find("SCHEMA_POPULATION") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    listProperties["schema_population"] = parseList(paramsMatch[1].str());
                }
            } else if (entity.find("FILE_POPULATION") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    std::string params = paramsMatch[1].str();
                    size_t pos1 = params.find(',');
                    size_t pos2 = params.find(',', pos1 + 1);
                    filePopulation["governing_schema"] = params.substr(0, pos1); filePopulation["governing_schema"].erase(std::remove(filePopulation["governing_schema"].begin(), filePopulation["governing_schema"].end(), '\''), filePopulation["governing_schema"].end());
                    filePopulation["determination_method"] = params.substr(pos1 + 1, pos2 - pos1 - 1); filePopulation["determination_method"].erase(std::remove(filePopulation["determination_method"].begin(), filePopulation["determination_method"].end(), '\''), filePopulation["determination_method"].end());
                    if (pos2 != std::string::npos) {
                        listProperties["governed_sections"] = parseList(params.substr(pos2 + 1));
                    }
                }
            } else if (entity.find("SECTION_LANGUAGE") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    std::string params = paramsMatch[1].str();
                    size_t pos = params.find(',');
                    simpleProperties["default_language"] = (pos != std::string::npos) ? params.substr(pos + 1) : "";
                    simpleProperties["default_language"].erase(std::remove(simpleProperties["default_language"].begin(), simpleProperties["default_language"].end(), '\''), simpleProperties["default_language"].end());
                }
            } else if (entity.find("SECTION_CONTEXT") != std::string::npos) {
                std::regex paramsRegex(R"\((.*)\)");
                std::smatch paramsMatch;
                if (std::regex_search(entity, paramsMatch, paramsRegex)) {
                    std::string params = paramsMatch[1].str();
                    size_t pos = params.find(',');
                    if (pos != std::string::npos) {
                        listProperties["context_identifiers"] = parseList(params.substr(pos + 1));
                    }
                }
            }
        }
    }

    void printProperties() {
        std::cout << "{\n";
        std::cout << "  \"description\": [" << std::endl;
        for (const auto& item : listProperties["description"]) std::cout << "    \"" << item << "\"," << std::endl;
        std::cout << "  ],\n";
        std::cout << "  \"implementation_level\": \"" << simpleProperties["implementation_level"] << "\",\n";
        std::cout << "  \"name\": \"" << simpleProperties["name"] << "\",\n";
        std::cout << "  \"time_stamp\": \"" << simpleProperties["time_stamp"] << "\",\n";
        std::cout << "  \"author\": [" << std::endl;
        for (const auto& item : listProperties["author"]) std::cout << "    \"" << item << "\"," << std::endl;
        std::cout << "  ],\n";
        std::cout << "  \"organization\": [" << std::endl;
        for (const auto& item : listProperties["organization"]) std::cout << "    \"" << item << "\"," << std::endl;
        std::cout << "  ],\n";
        std::cout << "  \"preprocessor_version\": \"" << simpleProperties["preprocessor_version"] << "\",\n";
        std::cout << "  \"originating_system\": \"" << simpleProperties["originating_system"] << "\",\n";
        std::cout << "  \"authorization\": \"" << simpleProperties["authorization"] << "\",\n";
        std::cout << "  \"schema_identifiers\": [" << std::endl;
        for (const auto& item : listProperties["schema_identifiers"]) std::cout << "    \"" << item << "\"," << std::endl;
        std::cout << "  ],\n";
        std::cout << "  \"schema_population\": [" << std::endl;
        for (const auto& item : listProperties["schema_population"]) std::cout << "    \"" << item << "\"," << std::endl;
        std::cout << "  ],\n";
        std::cout << "  \"file_population\": {\n";
        std::cout << "    \"governing_schema\": \"" << filePopulation["governing_schema"] << "\",\n";
        std::cout << "    \"determination_method\": \"" << filePopulation["determination_method"] << "\",\n";
        std::cout << "    \"governed_sections\": [" << std::endl;
        for (const auto& item : listProperties["governed_sections"]) std::cout << "      \"" << item << "\"," << std::endl;
        std::cout << "    ]\n";
        std::cout << "  },\n";
        std::cout << "  \"default_language\": \"" << simpleProperties["default_language"] << "\",\n";
        std::cout << "  \"context_identifiers\": [" << std::endl;
        for (const auto& item : listProperties["context_identifiers"]) std::cout << "    \"" << item << "\"," << std::endl;
        std::cout << "  ]\n";
        std::cout << "}" << std::endl;
    }

    void write(const std::string& newFilePath) {
        std::string newHeader = "HEADER;\n";
        newHeader += "FILE_DESCRIPTION(" + formatList(listProperties["description"]) + ",'" + simpleProperties["implementation_level"] + "');\n";
        newHeader += "FILE_NAME('" + simpleProperties["name"] + "','" + simpleProperties["time_stamp"] + "'," + formatList(listProperties["author"]) + "," + formatList(listProperties["organization"]) + ",'" + simpleProperties["preprocessor_version"] + "','" + simpleProperties["originating_system"] + "','" + simpleProperties["authorization"] + "');\n";
        newHeader += "FILE_SCHEMA(" + formatList(listProperties["schema_identifiers"]) + ");\n";
        if (!listProperties["schema_population"].empty()) {
            newHeader += "SCHEMA_POPULATION(" + formatList(listProperties["schema_population"]) + ");\n";
        }
        if (!filePopulation["governing_schema"].empty()) {
            newHeader += "FILE_POPULATION('" + filePopulation["governing_schema"] + "','" + filePopulation["determination_method"] + "'," + formatList(listProperties["governed_sections"]) + ");\n";
        }
        if (!simpleProperties["default_language"].empty()) {
            newHeader += "SECTION_LANGUAGE($,'" + simpleProperties["default_language"] + "');\n";
        }
        if (!listProperties["context_identifiers"].empty()) {
            newHeader += "SECTION_CONTEXT($," + formatList(listProperties["context_identifiers"]) + ");\n";
        }
        newHeader += "ENDSEC;\n";

        std::regex headerRegex(R"(HEADER;(.*?)ENDSEC;)", std::regex::dotall);
        std::string newContent = std::regex_replace(content, headerRegex, newHeader);

        std::ofstream outFile(newFilePath);
        outFile << newContent;
    }
};

// Example usage:
// int main() {
//     StpHandler handler("example.stp");
//     handler.printProperties();
//     handler.write("modified.stp");
//     return 0;
// }