[C++] Stream을 이용한 Typed Binary 데이터 읽고 쓰기

C++은 stream을 통해 반복자(iterator)를 이용해 데이터를  매우 유연하게 처리 할 수 있습니다. 이 스트림을 통해 데이터의 입력을 키보드나 파일 등을 통해 매우 유연하고 효과적으로 읽고 쓸 수 있는데요. 문제는 일반화된 문자열에 대한 읽고 쓰기에 중심을 두고 있다는 점입니다.  즉,  C++의 스트림을 이용해 실수 타입의 값으로 3.14159265라는 값을 파일에 저장하고자 한다면, 이 값을 메모리에 저장된 그대로의 실수형 바이너리 형식으로의 저장이 아닌 “3.14159265”라는 문자열로 저장되는 것이 C++ 개발자에게 흔히 노출된 방식이라는 것입니다.

다 이유가 있겠으나, 필자는 이 실수형 값을 메모리에 저장된 그대로의 실수형 바이너리 형식으로 파일에 저장하고, 이렇게 저장된 값을 다시 파일을 열어 꺼내와 화면에 표시하고자 합니다.

먼저 3.14159265 값을 d 드라이브의 data.bin에 저장하는 코드입니다.

#include <iostream>
#include <fstream>

int main()
{
	std::ofstream os("d:\\data.bin", std::ios::binary);

	double v = 3.141592625;
	os.write(reinterpret_cast<const char*>(&v), sizeof(double));

	os.close();
}

실행해 보면 data.bin 파일이 생성되어졌고, 파일 크기는 double의 바이트 크기와 동일한 8바이트인 것을 확인할 수 있습니다.

이제 이렇게 생성된 파일에서 다시 실수형 값을 읽어 보는 코드는 다음과 같습니다.

#include <iostream>
#include <fstream>

int main()
{
	std::ifstream is("d:\\data.bin", std::ios::binary);

	double v2;
	is.read(reinterpret_cast<char*>(&v2), sizeof(double));
	
	is.close();

	std::cout << v2;
}

여기서 C++의 template을 이용해 개선을 해보도록 하겠습니다. 즉, 다음과 같은 2개의 템플릿 함수를 추가합니다.

template<typename T>
std::ostream& write_typed_data(std::ostream& stream, const T& value) {
	return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
}

template<typename T>
std::istream & read_typed_data(std::istream& stream, T& value) {
	return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
}

네, write_typed_data와 read_typed_data는 각각 임이의 데이터 타입에 대해서 지정된 스트림에 메모리에 저장된 그대로의 구조인 바이너리 형식으로 쓰고 읽는 범용 함수입니다.

이 두 함수를 활용해 3.141592625 값을 쓰고 읽는 전체 코드를 다시 작성해 보면 다음과 같습니다.

#include <iostream>
#include <fstream>

template<typename T>
std::ostream& write_typed_data(std::ostream& stream, const T& value) {
	return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
}

template<typename T>
std::istream & read_typed_data(std::istream& stream, T& value) {
	return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
}

int main()
{
	// Write 
	std::ofstream os("d:\\data.bin", std::ios::binary);

	double v = 3.141592625;
	//os.write(reinterpret_cast<const char*>(&v), sizeof(double));
	write_typed_data(os, v);

	os.close();
	// <- test unit end

	// Read
	std::ifstream is("d:\\data.bin", std::ios::binary);

	double v2;
	read_typed_data(is, v2);
	//is.read(reinterpret_cast<char*>(&v2), sizeof(double));
	
	is.close();

	std::cout << v2;
	// <- test unit end
}

C++에서 스트림을 활용해 타입을 가지는 변수를 바이너리 형식으로 저장하는 위의 방식이 정석인지는 모르겠습니다. (이제와서 이게 무슨 소리? -_-;) reinterpret_cast를 사용했다는 점에서 상당히 의구심이 들기 때문인데요. 더욱 나은 방식이 있다면 제안해 주시기 바랍니다. reinterpret_cast는 포인터가 다른 포인터 형식으로 변환될 수 있도록 하거나 정수 계열 형식이 포인터 형식으로 변환될 수 있도록 하고 그 반대로도 변환될 수 있도록 하는 cast 연산자입니다.

[PostgreSQL] CREATE TABLE 예제

PostgreSQL에서 geometry 타입을 갖는 Table을 생성하고자 할때, 사용한 SQL 문에 대해 정리해 봅니다. 아래와 같은 형태의 테이블 2개에 대한 생성인데요.

위 형태의 테이블 2개에 대한 CREATE 문은 아래와 같습니다. 실제 용도는 네트워크 DB에 대한 Node, Link 관련 데이터를 저장하는 Table 생성에 대한 SQL입니다.

CREATE TABLE public.ecl_network_link
(
  networkid uuid PRIMARY KEY,  
  facility_layer character varying(30),
  snode_id uuid,
  snode_layer character varying(30),
  enode_id uuid,
  enode_layer character varying(30),
  the_geom geometry(MultiLineString)
);

ALTER TABLE public.ecl_network_link
  OWNER TO postgres;

CREATE INDEX ecl_network_link_geom_idx
  ON public.ecl_network_link
  USING gist
  (the_geom);
CREATE TABLE public.ecl_network_node
(
  networkid uuid PRIMARY KEY,  
  facility_layer character varying(30),
  the_geom geometry(Point)
);

ALTER TABLE public.ecl_network_node
  OWNER TO postgres;

CREATE INDEX ecl_network_node_geom_idx
  ON public.ecl_network_node
  USING gist
  (the_geom);

Javascript로 URL의 QueryString을 파싱하고 값 얻기

만약 웹브라우저에서 입력한 url이 다음과 같다고 가정하면..

http://localhost/map.html?x=925641&y=1666020&level=10

위의 url에서 QueryString으로 지정된 파라메터를 다음과 같은 JavaScript 코드로 얻을 수 있다고 합시다.

var qs = getQueryStringObject();
var x = qs.x; // 925641
var y = qs.y; // 1666020
var l = qs.level; // 10

이때 getQueryStringObject 함수는 다음과 같습니다.

function getQueryStringObject() {
    var a = window.location.search.substr(1).split('&');
    if (a == "") return {};
    var b = {};
    for (var i = 0; i < a.length; ++i) {
        var p = a[i].split('=', 2);
        if (p.length == 1)
            b[p[0]] = "";
        else
            b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
    }
    return b;
}

PostgreSQL에서 UUID Type 데이터 쿼리 및 업데이트

ID에 대한 고유한 값으로써 사용할만한 Type으로 PostgreSQL에서는 UUID 타입을 제공하는데요. 이 데이터 타입으로 조회하거나 Update하는 예는 아래와 같습니다.

select * from ecl_logic_node_p where networkid::text = '176f54c1-f333-426f-b4d5-29c365ec3ac3'
select * from ecl_logic_node_p where networkid = '176f54c1-f333-426f-b4d5-29c365ec3ac3'
select * from ecl_logic_node_p where networkid = '176f54c1-f333-426f-b4d5-29c365ec3ac3'::uuid

select networkid::text from ecl_logic_node_p

update ecl_logic_node_p set networkid = '176f54c1-f333-426f-b4d5-29c365ec3acf' where fid = 0

[미국 SGIP 2016 전시회] MicroGrid Platform 중 GIS Studio

금번 미국에서 SGIP 2016 전시회에 한국전력연구원에서 개발 중인 MicroGird Platform이 세계적으로 소개 되고 있습니다. MicroGrid Platform은 전력설비 감시를 위한 SCADA Platform, 전력 분석을 위한 SCL Studio, 전력 사업자 및 개발자들을 하나로 모아 참여할 수 있는 Smart Portal & API, 위치기반의 전력설비 관리를 위한 GIS Studio, 전력 사업자가 자신이 생산한 전력을 판매하기 위한 입찰 시장인 New Market Service 등으로 구성되는데요. 이중 회사에서는 GIS Studio를 개발하고 있습니다. 전시회를 위해 만든 팜플렛이 있기에 GIS Studio를 올려 봅니다.

웹 기반의 Map Viewer 및 Editor는 FingerEyes-Xr for HTML5를 사용했고, 공간서버(Spatial Server)는 GeoService-Xr이 이용되었으며, DBMS는 PostgreSQL + postGIS가 사용 되었습니다.

현재 전력 설비 표출과 신규 및 변경된 전력 설비를 웹에서 바로 편집할 수 있는 기능을 마무리하는 단계이고, 이제 역률, 부하, 전압, 주파수 등에 대한 전력 계측값 및 분석값을 지도 상에 어떻게 효과적으로 표현함으로써 해당 분야의 전문가에게 새로운 영감을 줄 수 있을지 상당한 고민이 반영되어야 할 단계입니다.