การ export ข้อมูลออกจาก dBForm

ที่ออฟฟิศ ตอนนี้มีเครื่อง PC เก่าๆ อยู่ตัวนึงที่ยังใช้ dbForm ซึ่งเป็นโปรแกรมสมัย Win98 อยู่ (ตัวเครื่องเป็น Win7 32 bit) และใช้เป็นโปรแกรมหลักในการออกบิล

พอช่วงที่อัปเกรดเป็น Windows 10 ได้ฟรีอยู่ก็มาลองอัปเกรดเครื่องนี้ดูด้วย ซึ่งการอัปเกรดก็ราบลื่นดีไม่มีปัญหาอะไร แต่กลับพบว่าโปรแกรมตัวที่ว่า ไม่สามารถอ่านภาษาไทยใน Windows 10 ได้ ก็เลยต้องย้อนกลับมาเป็น Windows 7 ก่อน

จุดที่น่าประทับใจคือการย้อนกลับมาเป็น Windows 7 นั้นช่างง่ายดาย และไม่พบปัญหาใดๆ ตราบเท่าที่ระหว่างที่ใช้ Windows 10 ไม่ได้ไปลบเพิ่มโปรแกรมอะไรก็ตาม (ยิ่งสั่งย้อนกลับเป็น Windows เวอร์ชั่นเก่าเร็วเท่าไหร ปัญหาจะยิ่งน้อยลงไปเท่านั้น)

อย่างไรก็ดี สุดท้ายแล้วเครื่องนี้มันจะต้องถูกอัปเกรดเป็น Windows 10 และเลิกใช้ dBForm ทิ้งไปเสีย การที่จะฝากอนาคตไว้กับโปรแกรมสมัย 20 ปีที่แล้วและไม่ได้มีการอัปเดตใด้ๆ ให้ใช้กับ OS ยุคใหม่ได้ ไม่เป็นเรื่องฉลาดเท่าใดนัก

ทางเลือกคงมีไม่มากนัก ไม่หาโปรแกรมสำเร็จรูปมาใช้งาน ก็เขียนโปรแกรมใหม่เอง แต่ปัญหาหลักคนเป็นเรื่องการ import/export ข้อมูลมากกว่า

ตัว dBForm เซฟฐานข้อมูลทั้งหมดเป็นนามสกุล .dbf ที่เกือบๆ จะเป็น plain text สามารถเปิดอ่านด้วย notepad ได้ทันทีว่าเก็บอะไรไว้ข้างในบ้าง เป็นโชคดีที่ถึงแม้ว่าเมื่อเปิดด้วยโปรแกรม text editor ทั่วไป จะเห็นทุก record เป็นบรรทัดเดียวยาวติดกันไปเรื่อยๆ แต่ ว่าแต่ละ record นั้นเป็น fixed width ทำให้เราพอจะสามารถนั่งเคาะ enter ตัดบรรทัดไปเรื่อยๆ (หรือใช้ text editor ที่รองรับการเล่น macro เช่น notepad++)

ในตอนแรกผมก็ลองใช้ macro ของ notepad++ ให้มันตัดบรรทัดใหม่ไปเรื่อยๆ แต่ก็พบว่าโปรแกรม text editor โดยทั่วไปเมื่อเจอกับบรรทัดที่ยาวมากๆ จะทำงานได้ช้ามาก ผมเลยเปลี่ยนวิธีใหม่ ไปเขียน nodejs แบบง่ายๆ สำหรับตัดบรรทัดเลยดีกว่า เขียนครั้งเดียวเก็บไว้ใช้คราวต่อไปได้ด้วย

// splitRecord.js
const fs = require('fs');
const readline = require('readline');
var splitRecord = function splitRecord(recordStart, recordWidth, recordTailing, infile, outfile) {
    var fileSizeInBytes = fs.statSync(infile)["size"];
    var fdr = fs.openSync(infile, 'r');
    var fdw = fs.openSync(outfile, 'w');
    var buffer = Buffer.alloc(recordWidth);
    var count = 0;
    while (true) {
        var offsetToRead = recordStart + (recordWidth + recordTailing) * count;
        if (offsetToRead >= fileSizeInBytes)
            break;
        fs.readSync(fdr, buffer, 0, recordWidth, offsetToRead);
        fs.appendFileSync(fdw, buffer);
        fs.appendFileSync(fdw, '\r\n');
        ++count;
    }
    console.log("split end: found %d records", count);
}
function csvSubstr(line, start, length, delim) {
    var str = line.substr(start, length).trim();
    if (str.length == 0)
        return '""';
    if (str.indexOf(delim) >= 0)
        return '="' + str + '"';
    else
        return str;
}
var convertCsv = function convertCsv(columns, infile, outfile) {
    const rl = readline.createInterface({
        input: fs.createReadStream(infile, { encoding: 'binary' })
    });
    var of = fs.createWriteStream(outfile, { flag: 'w', encoding: 'binary' })
    var delim = ';';
    rl.on('line', (line) => {
        var newLine = "";
        var startCol = 0;
        for (var col = 0; col < columns.length; ++col) {
            var colWidth = columns[col] - startCol;
            newLine += csvSubstr(line, startCol, colWidth, delim);
            newLine += delim; startCol += colWidth;
        }
        // last col
        var colWidth = line.length - startCol;
        newLine += csvSubstr(line, startCol, colWidth, delim);
        of.write(newLine);
        of.write('\r\n');
    }).on('close', () => {
        of.end();
    });
}
module.exports = {
    splitRecord: splitRecord,
    convertCsv: convertCsv,
};
// app.js
var input = {
    recordStart: 162,
    recordWidth: 137,
    col: [30, 110],
    dbf: './data-dbf/CODE.DBF',
    txt: './data-txt/CODE.DBF.txt',
    csv: './data-csv/CODE.DBF.csv',
},
const sr = require('./splitRecord.js');
console.log(input);
sr.splitRecord(input.recordStart, input.recordWidth, 1, input.dbf, input.txt);
sr.convertCsv(input.col, input.txt, input.csv);

script นี้เขียนมาแบบง่ายๆ ทำงานเป็น 2 pass รอบแรกจะตัดบรรทัดที่ยาวเป็น megabyte ออกเป็นบรรทัดๆ ตามที่เราตั้งค่า width ไว้

รอบที่สอง ก็ไปทำให้แต่ละบรรทัดกลายเป็น comma delimit อีกที จะได้เปิดอ่านด้วย excel ได้สะดวกๆ

เขียนจนเสร็จ เทสทุกอย่างเรียบร้อย

กลับมาพบทีหลังว่าไอ้ .dbf ของ dBForm จริงๆ แล้วมันคือ dbase เปิดอ่านด้วย MS Access ได้เลย ไม่ต้องมาเขียน nodejs อะไรให้วุ่นวายแบบนี้!

วะฮ่าๆๆ!

เอนทรี่นี้ เขียนมาเพื่อเตือนใจ และเผื่อคนอื่นที่กำลังอยากจะทำอะไรคล้ายๆ กันให้รู้ไว้

ส่วนไฟล์ .dbf ของโปรแกรมบัญชี Express นั้นเป็น MS Visual Foxpro ให้ไปหา Visual Foxpro Driver (ODBC) มาลงก็เปิดอ่านด้วย MS Access ได้เลยเช่นเดียวกัน แต่หายากหน่อยนะ เพราะ ms เลิกซัพพอทไปนานมากแล้ว

2 thoughts on “การ export ข้อมูลออกจาก dBForm”

  1. การจะเปิด ไฟล์ฺ .dbf ของ DBForm ด้วย MS Access สามารถเปิดด้วย Window 10 ได้ไหมครับ

    Reply
    • ได้ครับตอนที่ทำผมใช้ office 365 บน win 10 เปิดครับ

      Reply

Leave a Reply