Task 512: .PAR File Format

Task 512: .PAR File Format

1. List of All Properties of the .PAR File Format Intrinsic to Its File System

The .PAR file format refers to the SDSS (Sloan Digital Sky Survey) parameter file format, which is an ASCII-based format for storing structured data in a human- and machine-readable way. The phrase "intrinsic to its file system" appears to refer to the core structural and syntactic properties inherent to the format itself (possibly a mistranslation or typo for "intrinsic to the file format"). Based on the format specification, the intrinsic properties are:

  • ASCII-based: The file is plain text, ensuring it is both human-readable and machine-parsable without binary decoding.
  • Case Sensitivity: Table names are case-sensitive in definitions (typically all caps), but data lines may be interpreted flexibly by software (e.g., lowercase accepted as matching).
  • Whitespace Handling: Whitespace serves as a delimiter for elements; strings containing whitespace must be enclosed in double quotes.
  • Literal Values: Keyword values and data entries are treated as unprocessed strings (no automatic type conversion; handled by reading software).
  • Extensibility: Supports multiple tables and enumerations per file, fixed-length arrays, and enumerated types for restricting values.
  • Line-Based Structure: Data rows are preferably on single lines; multi-line continuation is supported via backslash () but not commonly generated by software.
  • No Multi-Dimensional Arrays: Limited to 1D arrays or arrays of strings (e.g., char[N][M] for string arrays).
  • Comment Integration: Comments can be inline after data, introduced by '#' (hash/pound sign).
  • Empty String Support: Represented via empty double quotes ("") or (deprecated) double braces ({{}}).
  • Enum String Retention: Enumerated values are stored as strings, not converted to integers.
  • Header Analogy: Keyword-value pairs form a header-like section similar to FITS headers.
  • Supported Data Types: short (16-bit int), int (32-bit int), long (64-bit int), float (32-bit float), double (64-bit float), char[N] (fixed-length string), enum (named enumeration).
  • Array Declarations: Fixed-length arrays use square brackets (e.g., int flags[2]); angle brackets (<>) are deprecated.
  • Enumeration Syntax: Defined like C enums, with all-caps values, comma-separated.
  • Table (Struct) Syntax: Defined like C structs, with table name in all caps; data lines start with the table name followed by values/arrays.
  • File Elements: May include comments/blank lines, keyword-value pairs, enumeration definitions, table definitions, and data lines in any order (though headers typically first).

These properties define the format's flexibility for astronomical data storage while maintaining simplicity.

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .PAR File Dump

Below is a complete HTML page with embedded JavaScript that can be embedded in a Ghost blog post (using the HTML code block). It allows users to drag and drop a .PAR file, parses it according to the SDSS .par specification, and dumps all properties (headers, enums, structs, data) to the screen in a readable format.

PAR File Parser
Drag and drop a .PAR file here

4. Python Class for .PAR File Handling

class ParFileHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.headers = {}
        self.enums = {}
        self.structs = {}
        self.data = []
        self._parse()

    def _parse(self):
        with open(self.filepath, 'r') as f:
            content = f.read()
        lines = [line.strip() for line in content.split('\n') if line.strip() and not line.strip().startswith('#')]
        current_mode = 'header'
        current_struct = None
        for line in lines:
            if line.startswith('typedef enum'):
                current_mode = 'enum'
                enum_name = line.split('}')[1].strip().rstrip(';')
                enum_values = [v.strip() for v in line.split('{')[1].split('}')[0].split(',')]
                self.enums[enum_name] = enum_values
            elif line.startswith('typedef struct'):
                current_mode = 'struct'
                struct_name = line.split('}')[1].strip().rstrip(';')
                self.structs[struct_name] = []
                current_struct = struct_name
            elif current_mode == 'struct' and ';' in line:
                type_name = line.rstrip(';').strip().split()
                array_size = None
                if '[' in type_name[1]:
                    name, array_size = type_name[1].split('[')
                    array_size = array_size.rstrip(']')
                else:
                    name = type_name[1]
                self.structs[current_struct].append({'type': type_name[0], 'name': name, 'array_size': array_size})
            elif current_mode in ['header', 'data'] and ' ' in line:
                parts = line.split()
                table_name = parts[0].upper()
                if table_name in self.structs:
                    current_mode = 'data'
                    values = ' '.join(parts[1:]).replace('{', '').replace('}', '').replace('"', '').split()
                    self.data.append({'table': table_name, 'values': values})
                else:
                    key = parts[0]
                    value = ' '.join(parts[1:])
                    self.headers[key] = value

    def print_properties(self):
        print("Headers:", self.headers)
        print("Enums:", self.enums)
        print("Structs:", self.structs)
        print("Data:", self.data)

    def write(self, new_filepath):
        with open(new_filepath, 'w') as f:
            for key, value in self.headers.items():
                f.write(f"{key} {value}\n")
            for enum_name, values in self.enums.items():
                f.write(f"typedef enum {{ {', '.join(values)} }} {enum_name};\n")
            for struct_name, fields in self.structs.items():
                f.write("typedef struct {\n")
                for field in fields:
                    array = f"[{field['array_size']}]" if field['array_size'] else ''
                    f.write(f"    {field['type']} {field['name']}{array};\n")
                f.write(f"}} {struct_name};\n")
            for entry in self.data:
                values_str = ' '.join(entry['values'])
                f.write(f"{entry['table']} {{ {values_str} }}\n")

# Example usage:
# handler = ParFileHandler('example.par')
# handler.print_properties()
# handler.write('new.par')

5. Java Class for .PAR File Handling

import java.io.*;
import java.util.*;

public class ParFileHandler {
    private String filepath;
    private Map<String, String> headers = new HashMap<>();
    private Map<String, List<String>> enums = new HashMap<>();
    private Map<String, List<Map<String, String>>> structs = new HashMap<>();
    private List<Map<String, Object>> data = new ArrayList<>();

    public ParFileHandler(String filepath) {
        this.filepath = filepath;
        parse();
    }

    private void parse() {
        try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
            String line;
            String currentMode = "header";
            String currentStruct = null;
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (line.isEmpty() || line.startsWith("#")) continue;
                if (line.startsWith("typedef enum")) {
                    currentMode = "enum";
                    String enumName = line.split("}")[1].trim().replace(";", "");
                    String[] values = line.split("\\{")[1].split("}")[0].split(",");
                    enums.put(enumName, Arrays.asList(Arrays.stream(values).map(String::trim).toArray(String[]::new)));
                } else if (line.startsWith("typedef struct")) {
                    currentMode = "struct";
                    String structName = line.split("}")[1].trim().replace(";", "");
                    structs.put(structName, new ArrayList<>());
                    currentStruct = structName;
                } else if (currentMode.equals("struct") && line.endsWith(";")) {
                    String fieldLine = line.replace(";", "").trim();
                    String[] parts = fieldLine.split("\\s+");
                    String type = parts[0];
                    String name = parts[1];
                    String arraySize = null;
                    if (name.contains("[")) {
                        arraySize = name.split("\\[")[1].replace("]", "");
                        name = name.split("\\[")[0];
                    }
                    Map<String, String> field = new HashMap<>();
                    field.put("type", type);
                    field.put("name", name);
                    if (arraySize != null) field.put("array_size", arraySize);
                    structs.get(currentStruct).add(field);
                } else {
                    String[] parts = line.split("\\s+", 2);
                    String key = parts[0].toUpperCase();
                    if (structs.containsKey(key)) {
                        currentMode = "data";
                        String valuesStr = parts[1].replace("{", "").replace("}", "").replace("\"", "").trim();
                        String[] values = valuesStr.split("\\s+");
                        Map<String, Object> entry = new HashMap<>();
                        entry.put("table", key);
                        entry.put("values", Arrays.asList(values));
                        data.add(entry);
                    } else if (parts.length >= 2) {
                        headers.put(parts[0], parts[1]);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void printProperties() {
        System.out.println("Headers: " + headers);
        System.out.println("Enums: " + enums);
        System.out.println("Structs: " + structs);
        System.out.println("Data: " + data);
    }

    public void write(String newFilepath) {
        try (PrintWriter writer = new PrintWriter(new File(newFilepath))) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                writer.println(entry.getKey() + " " + entry.getValue());
            }
            for (Map.Entry<String, List<String>> entry : enums.entrySet()) {
                writer.println("typedef enum { " + String.join(", ", entry.getValue()) + " } " + entry.getKey() + ";");
            }
            for (Map.Entry<String, List<Map<String, String>>> entry : structs.entrySet()) {
                writer.println("typedef struct {");
                for (Map<String, String> field : entry.getValue()) {
                    String array = field.containsKey("array_size") ? "[" + field.get("array_size") + "]" : "";
                    writer.println("    " + field.get("type") + " " + field.get("name") + array + ";");
                }
                writer.println("} " + entry.getKey() + ";");
            }
            for (Map<String, Object> entry : data) {
                String valuesStr = String.join(" ", (List<String>) entry.get("values"));
                writer.println(entry.get("table") + " { " + valuesStr + " }");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    // Example usage:
    // public static void main(String[] args) {
    //     ParFileHandler handler = new ParFileHandler("example.par");
    //     handler.printProperties();
    //     handler.write("new.par");
    // }
}

6. JavaScript Class for .PAR File Handling

class ParFileHandler {
    constructor(filepath) {
        this.filepath = filepath;
        this.headers = {};
        this.enums = {};
        this.structs = {};
        this.data = [];
        // Note: For Node.js, use fs module to read file
        const fs = require('fs');
        const content = fs.readFileSync(filepath, 'utf8');
        this._parse(content);
    }

    _parse(content) {
        const lines = content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
        let currentMode = 'header';
        let currentStruct = null;
        lines.forEach(line => {
            if (line.startsWith('typedef enum')) {
                currentMode = 'enum';
                const enumName = line.match(/}\s*(\w+);/)[1];
                const values = line.match(/\{([^}]+)\}/)[1].split(',').map(v => v.trim());
                this.enums[enumName] = values;
            } else if (line.startsWith('typedef struct')) {
                currentMode = 'struct';
                const structName = line.match(/}\s*(\w+);/)[1];
                this.structs[structName] = [];
                currentStruct = structName;
            } else if (currentMode === 'struct' && line.endsWith(';')) {
                const parts = line.replace(';', '').trim().split(/\s+/);
                let type = parts[0];
                let name = parts[1];
                let arraySize = null;
                if (name.includes('[')) {
                    arraySize = name.split('[')[1].replace(']', '');
                    name = name.split('[')[0];
                }
                this.structs[currentStruct].push({ type, name, arraySize });
            } else {
                const parts = line.split(/\s+/, 2);
                const key = parts[0].toUpperCase();
                if (this.structs[key]) {
                    currentMode = 'data';
                    const valuesStr = parts[1].replace(/\{|\}|"/g, '').trim();
                    const values = valuesStr.split(/\s+/);
                    this.data.push({ table: key, values });
                } else if (parts.length >= 2) {
                    this.headers[parts[0]] = parts[1];
                }
            }
        });
    }

    printProperties() {
        console.log('Headers:', this.headers);
        console.log('Enums:', this.enums);
        console.log('Structs:', this.structs);
        console.log('Data:', this.data);
    }

    write(newFilepath) {
        let output = '';
        Object.entries(this.headers).forEach(([key, value]) => output += `${key} ${value}\n`);
        Object.entries(this.enums).forEach(([name, values]) => output += `typedef enum { ${values.join(', ')} } ${name};\n`);
        Object.entries(this.structs).forEach(([name, fields]) => {
            output += 'typedef struct {\n';
            fields.forEach(field => {
                const array = field.arraySize ? `[${field.arraySize}]` : '';
                output += `    ${field.type} ${field.name}${array};\n`;
            });
            output += `} ${name};\n`;
        });
        this.data.forEach(entry => {
            const valuesStr = entry.values.join(' ');
            output += `${entry.table} { ${valuesStr} }\n`;
        });
        const fs = require('fs');
        fs.writeFileSync(newFilepath, output);
    }
}

// Example usage (Node.js):
// const handler = new ParFileHandler('example.par');
// handler.printProperties();
// handler.write('new.par');

7. C Class (C++) for .PAR File Handling

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>

class ParFileHandler {
private:
    std::string filepath;
    std::map<std::string, std::string> headers;
    std::map<std::string, std::vector<std::string>> enums;
    std::map<std::string, std::vector<std::map<std::string, std::string>>> structs;
    std::vector<std::map<std::string, std::vector<std::string>>> data;

    void parse() {
        std::ifstream file(filepath);
        std::string line;
        std::string currentMode = "header";
        std::string currentStruct;
        while (std::getline(file, line)) {
            std::istringstream iss(line);
            line.erase(0, line.find_first_not_of(" \t"));
            if (line.empty() || line[0] == '#') continue;
            if (line.find("typedef enum") != std::string::npos) {
                currentMode = "enum";
                size_t bracePos = line.find('{');
                size_t endBracePos = line.find('}');
                std::string enumName = line.substr(endBracePos + 1);
                enumName = enumName.substr(0, enumName.find(';'));
                enumName.erase(remove_if(enumName.begin(), enumName.end(), isspace), enumName.end());
                std::string valuesStr = line.substr(bracePos + 1, endBracePos - bracePos - 1);
                std::vector<std::string> values;
                std::istringstream valuesIss(valuesStr);
                std::string value;
                while (std::getline(valuesIss, value, ',')) {
                    value.erase(remove_if(value.begin(), value.end(), isspace), value.end());
                    values.push_back(value);
                }
                enums[enumName] = values;
            } else if (line.find("typedef struct") != std::string::npos) {
                currentMode = "struct";
                size_t endBracePos = line.find('}');
                std::string structName = line.substr(endBracePos + 1);
                structName = structName.substr(0, structName.find(';'));
                structName.erase(remove_if(structName.begin(), structName.end(), isspace), structName.end());
                structs[structName] = {};
                currentStruct = structName;
            } else if (currentMode == "struct" && line.find(';') != std::string::npos) {
                line = line.substr(0, line.find(';'));
                std::istringstream fieldIss(line);
                std::string type, name;
                fieldIss >> type >> name;
                std::string arraySize;
                if (name.find('[') != std::string::npos) {
                    size_t bracketPos = name.find('[');
                    arraySize = name.substr(bracketPos + 1, name.find(']') - bracketPos - 1);
                    name = name.substr(0, bracketPos);
                }
                std::map<std::string, std::string> field;
                field["type"] = type;
                field["name"] = name;
                if (!arraySize.empty()) field["array_size"] = arraySize;
                structs[currentStruct].push_back(field);
            } else {
                std::istringstream partsIss(line);
                std::string key;
                partsIss >> key;
                key = std::string(toupper(key[0])) + key.substr(1); // Approximate upper
                std::string rest;
                std::getline(partsIss, rest);
                if (structs.find(key) != structs.end()) {
                    currentMode = "data";
                    rest.erase(remove(rest.begin(), rest.end(), '{'), rest.end());
                    rest.erase(remove(rest.begin(), rest.end(), '}'), rest.end());
                    rest.erase(remove(rest.begin(), rest.end(), '"'), rest.end());
                    std::istringstream valuesIss(rest);
                    std::vector<std::string> values;
                    std::string val;
                    while (valuesIss >> val) values.push_back(val);
                    std::map<std::string, std::vector<std::string>> entry;
                    entry["table"] = key;
                    entry["values"] = values;
                    data.push_back(entry);
                } else {
                    headers[key] = rest;
                }
            }
        }
    }

public:
    ParFileHandler(std::string fp) : filepath(fp) { parse(); }

    void printProperties() {
        std::cout << "Headers:" << std::endl;
        for (auto& h : headers) std::cout << h.first << ": " << h.second << std::endl;
        std::cout << "Enums:" << std::endl;
        for (auto& e : enums) {
            std::cout << e.first << ": ";
            for (auto& v : e.second) std::cout << v << " ";
            std::cout << std::endl;
        }
        std::cout << "Structs:" << std::endl;
        for (auto& s : structs) {
            std::cout << s.first << ":" << std::endl;
            for (auto& f : s.second) {
                std::cout << "  type: " << f.at("type") << ", name: " << f.at("name");
                if (f.count("array_size")) std::cout << ", array_size: " << f.at("array_size");
                std::cout << std::endl;
            }
        }
        std::cout << "Data:" << std::endl;
        for (auto& d : data) {
            std::cout << d.at("table") << ": ";
            for (auto& v : d.at("values")) std::cout << v << " ";
            std::cout << std::endl;
        }
    }

    void write(std::string newFilepath) {
        std::ofstream file(newFilepath);
        for (auto& h : headers) file << h.first << " " << h.second << "\n";
        for (auto& e : enums) {
            file << "typedef enum { " ;
            for (size_t i = 0; i < e.second.size(); ++i) {
                file << e.second[i];
                if (i < e.second.size() - 1) file << ", ";
            }
            file << " } " << e.first << ";\n";
        }
        for (auto& s : structs) {
            file << "typedef struct {\n";
            for (auto& f : s.second) {
                file << "    " << f.at("type") << " " << f.at("name");
                if (f.count("array_size")) file << "[" << f.at("array_size") << "]";
                file << ";\n";
            }
            file << "} " << s.first << ";\n";
        }
        for (auto& d : data) {
            file << d.at("table") << " { ";
            for (auto& v : d.at("values")) file << v << " ";
            file << "}\n";
        }
        file.close();
    }
};

// Example usage:
// int main() {
//     ParFileHandler handler("example.par");
//     handler.printProperties();
//     handler.write("new.par");
//     return 0;
// }