Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

소개

다음과 같은 형식의 설정파일 내용을 읽을 수 있는 프로그램을 만들기를 원했다. 꽤 일반적인 형식이고, 아마도 관련 perl 모듈을 찾을 수 있을거라 생각되었지만, Perl을 이용한 자료구조다루는 법도 익힐겸해서 직접 만들어 보기로 했다.

[section]
name1=value
name2=value1,value2
아마 C++을 이용했다면, 분명히 STL(:12)을 이용해서 vector(:12)의 map(:12)혹은 map의 map 형식으로 구현했을 것이다. 일반적인 자료구조로 하자면 배열의 hash, hash 의 hash일 것이다. 예전에 C++로 간단히 구현해본게 있는데, 아마 이 코드의 Perl 버젼이 되지 않을까 싶다.

hash 초기화

가장 간단한방법은 비어있는 list를 할당하는 것이다.
my %hash = ();

키와 값 추가 하기

$hash{'key'} = 'value';
$hash{$key} = $value;
hash의 레퍼런스일 경우에는 C(:12)의 포인터(:12)에서처럼 '->'를 사용한다.
$href->{'key'} = 'value';
$href->{$key} = $value;

한번에 여러개의 키,값 추가하기

%hash = ('key1', 'value1', 'key2', 'value2', 'key3', 'value3');
key, value 순으로 나열이 된다. 배열과 전혀차이가 없음을 알 수 있다. n번째 원소는 key n+1번째 원소는 value로 해석이 될 뿐이다.

이러한 hash의 성질을 이용해서, perl에서 구조체를 구현할 수 있다. 참고로 perl은 C(:12)에서의 구조체를 지원하지 않는다. 예를 들어서 사용자 정보를 위한 userInfo 구조체가 필요하다면, 다음과 같을 것이다.
%hash = (
	'name', '',
	'address', '',
	'age', '',
	'tel', ''
);
다음은 간단한 테스트 프로그램이다.
#!/usr/bin/perl

%userInfo = (
  'name', '',
  'address', '',
  'age', '',
  'tel', ''
);

$userInfo{'name'} = 'yundream';
$userInfo{'address'} = 'seoul seo-cho dong';
$userInfo{'age'} = 19;
$userInfo{'tel'} = '11-111-1111';

for $name(keys %userInfo)
{
  print $name,"=>", $userInfo{$name},"\n";
}

# 해쉬가 배열이라는 것을 확인하기 위해서. 
foreach(%userInfo)
{
  print $_,"\n";
}

hash의 hash

처음 지원할려고 했던, 설정파일을 다시 살펴보도록 하자.
[section1]
name1=value1
name2=value2
[section2]
name1=value1
name2=value2
name1=value는 name1이 가 되고 value가 이 되는 hash자료구조를 가진다. 그리고 이것은 다시 section을 key로 하는 hash자료구조에 들어가게 된다. 결국 hash의 hash가 된다.
section1 : name1=value1 name2=value2
section2 : name1=value1 name2=value2

다음은 hash의 hash 자료구조를 지원하는 설정파일 읽기 함수다. perl이니까 서브루틴이라고 하는게 좋을것 같다.
sub getCfg
{
  my ($cfgfile) = @_;
  open(FD, "<$cfgfile");
  $rSection = "";
  %cfgmap=();
  while($line = <FD>)
  {
    # 읽어들인 라인이 section 인지를 확인한다.
    if($line =~ /\[[a-zA-Z0-9]+\]/) 
    {
      $line =~ s/\n|\[|\]//g;
      push(@cfgSection, $cfgValue);
      $Section = $line;
      $rSection = $Section;
      next;
    }
    # 설정값이라면 '='를 기준으로 key와 value를 구분해낸다음 
    # 현재 section 이름을 key로 하는 hash에 집어 넣는다.
    if($line =~ /[a-zA-Z0-9]+=/)
    {
      $line =~ s/\s//g;
      ($key, $value) = split '=', $line;
      $cfgmap{$Section}{$key} = $value;
    }
  }
  close(FD);
  return \%cfgmap;
}

STL의 map을 사용해본 경험이 있다면, perl의 hash of hash 자료구조가 헷갈릴 수 있을 것 같다. 왜냐하면 STL이라면 다음과 같이 구현될 것이기 때문이다. perl과는 다르다는 것을 알 수 있을 것이다. 나도 처음엔 이것 때문에 꽤나 어리둥절 했었던 기억이 있다.
map.sub{key1} = value1;
map.sub{key2} = value2;
map.main{key} = map.sub

hash의 hash access

위의 getCfg 서브루틴을 이용해서 hash of hash의 aceess방법에 대해서 알아보겠다. 기본적으로 두번의 루프를 돌려야 원하는 값을 얻어올 수 있다.
my $cfg = getCfg("config.cfg");
for my $section (keys %$cfg)
{
  print "Section $section\n";
  for my $name (keys %{$cfg->{$section}})
  {
    print "$name = $cfg->{$section}->{$name}\n";
  }
}

getCfg가 레퍼런스를 리턴했기 때문에,->연산자를 써서 값에 접근했다는 것외에는 별 특이사항이 없다.

관련글

  1. aero님이 작성한 Perl에 Hash는 없다? 내가 작성한 문서에서는 편의상 hash와 array가 같은 것이라고 했는데, 이 문서에 hash와 array의 차이점에 대해서 자세히 설명하고 있다. 암튼 perl은 복잡해..