I am a huge fan of Ben Marwick. He has so many useful pieces of code for the programming archaeologist or historian!

Edit July 17 1.20 pm: Mea culpa: I originally titled this post, ‘Doing OCR within R’. But, what I’m describing below – that’s not OCR. That’s extracting text from pdfs. It’s very fast and efficient, but it’s not OCR. So, brain fart. But I leave the remainder of the post as it was. For command line OCR (really, actual OCR) on a Mac, see the link to Ben Schmidt’s piece at the bottom. Sorry.

Edit July 17 10 pm: I am now an even bigger fan of Ben’s. He’s updated his script to either a) perform OCR by calling Tesseract from within R or b) grab the text layer from a pdf image. So this post no longer misleads. Thank you Ben!

Object Character Recognition, or OCR, is something that most historians will need to use at some point when working with digital documents. That is, you will often encounter pdf files of texts that you wish to work with in more detail (digitized newspapers, for instance). Often, there is a layer within the pdf image containing the text already: if you can highlight text by clicking and dragging over the image, you can copy and paste the text from the image. But this is often not the case, or worse, you have tens or hundreds or even thousands of documents to examine. There is commercial software that can do this for you, but it can be quite expensive

One way of doing OCR on your own machine with free tools, is to use Ben Marwick’s pdf-2-text-or-csv.r script for the R programming language. Marwick’s script uses R as wrapper for the Xpdf programme from Foolabs. Xpdf is a pdf viewer, much like Adobe Acrobat. Using Xpdf on its own can be quite tricky, so Marwick’s script will feed your pdf files to Xpdf, and have Xpdf perform the text extraction. For OCR, the script acts as a wrapper for Tesseract, which is not an easy piece of software to work with. There’s a final part to Marwick’s script that will pre-process the resulting text files for various kinds of text analysis, but you can ignore that part for now.

  1. Make sure you have R downloaded and installed on your machine (available from http://www.r-project.org/)
  2. Make sure you have Xpdf downloaded and installed (available from ftp://ftp.foolabs.com/pub/xpdf/xpdfbin-win-3.04.zip ). Make a note of where you unzipped it. In particular, you are looking for the location of the file ‘pdftotext.exe’. Also, make sure you know where ‘pdftoppm’ is located too (it’s in that download).
  3. Download and install Tesseract https://code.google.com/p/tesseract-ocr/ 
  4. Download and install Imagemagick http://www.imagemagick.org/
  5. Have a folder with the pdfs you wish to extract text from.
  6. Open R, and paste Marwick’s script into the script editor window.
  7. Make sure you adjust the path for “dest” and the path to “pdftotext.exe” to the correct location
  8. Run the script! But read the script carefully and make sure you run the bits you need. Ben has commented out the code very well, so it should be fairly straightforward.

Obviously, the above is framed for Windows users. For Mac users, the steps are all the same, except that you use the version of Xpdf, Tesseract, and Imagemagick built for IOS, and your paths to the other software are going to be different. And of course you’re using R for Mac, which means the ‘shell’ commands have to be swapped to ‘system’! (As of July 2014, the Xpdf file for Mac that you want is at ftp://ftp.foolabs.com/pub/xpdf/xpdfbin-mac-3.04.tar.gz ) I’m not 100% certain of any other Mac/PC differences in the R script – these should only exist at those points where R is calling on other resources (rather than on R packages). Caveat lector, eh?

The full R script may be found athttps://gist.github.com/benmarwick/11333467. So here is the section that does the text extraction from pdf images (ie, you can copy and highlight text in the pdf):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
###Note: there's some preprocessing that I (sg) haven't shown here: go see the original gist
 
################# Wait! ####################################
# Before proceeding, make sure you have a copy of pdf2text
# on your computer! Details: https://en.wikipedia.org/wiki/Pdftotext
 
# Tell R what folder contains your 1000s of PDFs
dest <- "G:/somehere/with/many/PDFs"
 
# make a vector of PDF file names
myfiles <- list.files(path = dest, pattern = "pdf",  full.names = TRUE)
 
# now there are a few options...
 
############### PDF to TXT #################################
# convert each PDF file that is named in the vector into a text file
# text file is created in the same directory as the PDFs
# note that my pdftotext.exe is in a different location to yours
lapply(myfiles, function(i) system(paste('"C:/Program Files/xpdf/bin64/pdftotext.exe"', paste0('"', i, '"')), wait = FALSE) )
 
# where are the txt files you just made?
dest # in this folder

And here’s the bit that does the OCR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre>
                     ##### Wait! #####
# Before proceeding, make sure you have a copy of Tesseract
# on your computer! Details & download:
# and a copy of ImageMagick: http://www.imagemagick.org/
# and a copy of pdftoppm on your computer!
# And then after installing those three, restart to
# ensure R can find them on your path.
# And note that this process can be quite slow...
 
# PDF filenames can't have spaces in them for these operations
# so let's get rid of the spaces in the filenames
 
sapply(myfiles, FUN = function(i){
  file.rename(from = i, to =  paste0(dirname(i), "/", gsub(" ", "", basename(i))))
})
 
# get the PDF file names without spaces
myfiles <- list.files(path = dest, pattern = "pdf",  full.names = TRUE)
 
# Now we can do the OCR to the renamed PDF files. Don't worry
# if you get messages like 'Config Error: No display
# font for...' it's nothing to worry about
 
lapply(myfiles, function(i){
  # convert pdf to ppm (an image format), using
  shell(shQuote(paste0("pdftoppm ", i, " -f 1 -l 10 -r 600 ocrbook")))
  # convert ppm to tif ready for tesseract
  shell(shQuote(paste0("convert *.ppm ", i, ".tif")))
  # convert tif to text file
  shell(shQuote(paste0("tesseract ", i, ".tif ", i, " -l eng")))
  # delete tif file
  file.remove(paste0(i, ".tif" ))
  })
 
# where are the txt files you just made?
dest # in this folder

Besides showing how to do your own OCR, Marwick’s script shows some of the power of R for doing more than statistics. Mac users might be interested in Ben Schmidt’s tutorial ‘Command-line OCR on a Mac’ from his digital history graduate seminar at Northeastern University, online athttp://benschmidt.org/dighist13/?page_id=129.


출처 : Extracting Text from PDFs; Doing OCR; all within R


바로 : 요즘은 PDF에서 곧장 텍스트를 추출할 수 있다. 그러나 다양한 이유로 텍스트 추출이 어려운 경우가 있다. 물론 이를 지원하는 수 많은 유료소프트웨어가 있기는 하지만......돈 없는 우리가 불법행위를 하지 않으면서 텍스트를 추출하는 방법은 무엇인가?! 저자는 무료툴인 R을 통해서 텍스트를 추출하는 방법에 대해서 서술하고 있다. 

가칭 "한국 역사 관계 데이터베이스(Korea Historical Network DataBase, KHNDB)"


본 내용은 한국 역사에서 출현하는 관계를 데이터베이스화 하기 위한 기초작업이다. 관계는 인물, 기관, 사물 등 다양한 영역의 관계를 의미한다. 그러나 역량의 한계로 우선적으로 특정 DB들을 바탕으로 인물관계 데이터베이스만 구축 시도한다.(그래도 10만건……혼자서 10만건 하려니 죽을 거 같…-_)


아래의 규칙들은 추후 수정될 수도 있다.

혹시 괜찮은 아이디어나 관련 정보가 있으시면 언제든지....조언 좀..ㅠㅠ

 


+ 인물 ID 값 규칙

예시 : 1가귀U9999X0

남녀구별 : 남자(1), 여자(2), 혼합(3), 미상(9)

한글이름 :  이름(가귀) 명시. 3자리 이상의 이름도 모두 서술

보조연도 : B(생년) or D(몰년) or U(미상) B1545 : 생년(Birth) 1545년을 의미, 몰년일 경우 D와 서기연도, 생몰년 미상일 경우 U9999가 옴

X0 : 중복되는 인물이 있을 경우 X1, X2로 증가. X9는 기존DB에 없는 것을 표기

 


 


+ 사회관계 ID값 규칙

예시 : S0101

사회관계고유값 : S(society)

분류코드값 : 분류코드표 참조

ID

한국어

영어

중국어

S0100

사회관계(포괄)

Associations (General)

社會關係籠統

S0101

사회관계(포괄)

Associations (General)

社會關係籠統

S0102

모두 X 구성원

Association through common membership

……之成員

S0103

사회교제

Social Interactions

社會交際

S0200

학술관계류

Scholarship

學術關係類

S0201

학술(포괄)

Scholarship (General)

學術籠統

S0202

사도관계(스승)

Teacher-Student

師生關係

S0203

사도관계(제자)

Teacher-Student

師生關係

S0204

학술교류

Intellectual Affiliations

學術交往

S0205

학술주제밀접

Association by Scholarly Topic

學術主題相近

S0206

모두 X 구성원

Association through common membership

……之成員

S0207

학술원조

Academic Patronage

學術襄助

S0208

문학예술교류

Literary and Artistic Affiliations

文學藝術交往

S0209

학술비판

Intellectual Attacks

學術攻訐

S0300

친구관계류

Friendship

朋友關係類

S0301

친구관계(포괄)

Friendship(General)

朋友關係籠統

S0400

정치관계류

Politics

政治關係類

S0401

정치관계(포괄)

Politics (General)

政治關係籠統

S0402

관직관계(동급)

Connection via office (equal)

官場關係平級

S0403

관직관계(부하)

Connection via office (subordinate)

官場關係下屬

S0404

관직관계(상사)

Connection via office (superior)

官場關係上司

S0405

정치원조

Supportive political association

政治奧援

S0406

천거보증

Recommendation and sponsorship

舉保

S0407

정치대항

Oppositional political association

政治對抗

S0500

저술관계류

Writings

著述關係類

S0501

저술관계(포괄)

Writings (General)

著述關係籠統

S0502

일반문장

Commemorative Texts

記詠文字

S0503

묘지문장

Epitaphs

墓誌文字

S0504

서발문문장

Prefaces/Postfaces

序跋文字

S0505

예의문장

Ritual Texts

禮儀文字

S0506

전기문장

Biographical Texts

傳記文字

S0507

논설문장

Explanatory Texts

論說文字

S0508

모토문장

Mottos

箴銘文字

S0509

서찰문장

Correspondences

書札文字

S0510

계약(보상)문장

Occasional Texts

應酬文字

S0600

군사관계류

Military

軍事關係類

S0601

군사관계(포괄)

Military (General)

軍事關係籠統

S0602

군사지원

Supportive military association

軍事支持

S0603

군사대항

Oppositional military association

軍事對抗

S0700

의료관계류

Medicine

醫療關係類

S0701

의료관계(포괄)

Medicine (General)

醫療關係籠統

S0800

종교관계류

Religion

教關係

S0801

종교관계(포괄)

Religion (General)

教關係籠統

S0900

가정관계류

Family

家庭關係類

S0901

가정관계(포괄)

Family (General)

家庭關係籠統

S1000

재정관계류

Finance

財務關係類

S1001

재정관계(포괄)

Finance (General)

財務關係籠統

S1100

예술관계류

 

 

S1101

예술관계(포괄)

 

 

 


 






+ 혈연관계 ID값 규칙

B0201_0302

혈연관계 고유값 : B

촌수 : 01, 02, 03 (단순촌수계산. 법도무시)

관계속성(상하위) : 0(통합), 1(상위) 2(동급) 3(하위)

관계속성(구분) : 무속성(0) 친계(1) 외계(2) 부당계(3) 처당계(4)

구분자 : _

남녀구별 : 통합(0), 남자(1), 여자(2)

고유값 : 3자리


사실 위의 규칙으로 확장시 1000개정도는 무난히 넘을 것이다. 일단 몇 개만 예시로 제시한 것일 뿐이다. CBDB의 혈연코드가 559개였는데, 본인이 4촌이하 주요정보만 대략 했는데도 170여개이다. 만약 본인이 마음만 먹으면 정말 1000개 만들수도 있다. 하지만 실제로 그런 관계가 문헌이나 자료에 명시된 경우는 거의 없기에 사실상 아래 코드로 90%이상 커버 가능하다고 본다. 

 

코드

설명

B9999_9999

미상

B0000_0000

친척

B0000_0100

姻親(통합)

B0000_0200

親族

B0000_0300

親屬

B0000_0400

直系親族

B0001_0000

直系血族

B0011_0000

直系尊屬

B0031_0000

直系卑屬

B0003_0000

부당(夫黨)

B0004_0000

처당(妻黨)

B0020_0000

부부(통합)

B0020_1000

丈夫(통합)

B0020_1001

未婚夫

B0020_1051

第二任丈夫

B0020_1052

第三任丈夫

B0020_2000

妻子(통합)

B0020_2001

未婚妻

B0020_2010

B0110_0000

부모(통합)

B0110_1000

(통합)

B0110_1001

生父

B0110_1002

養父

B0110_1003

繼父

B0110_1004

嗣父

B0110_2000

(통합)

B0110_2001

生母

B0110_2002

養母

B0110_2003

繼母

B0110_2004

嗣母

B0110_2005

乳母,유모

B0114_0000

장인-장모(통합)

B0114_1000

장인

B0114_2000

장모

B0113_0000

시아버지-시어머니

B0113_1000

시아버지(시부)

B0113_2000

시어머니(시모)

B0130_0000

자식(통합)

B0130_1000

아들(통합)

B0130_1001

獨子

B0130_1002

繼子

B0130_1003

養子(非嗣子)

B0130_1004

庶子

B0130_1005

私生子

B0130_1101

長子; 第一子

B0130_1102

次子

B0130_1103

三子

B0130_1104

四子

B0130_1105

五子

B0130_1106

六子

B0130_1107

七子

B0130_1108

八子

B0130_1109

九子

B0130_2000

(통합)

B0130_2001

獨女

B0130_2002

繼女

B0130_2003

養女(非嗣女)

B0130_2004

庶女

B0130_2005

私生女

B0130_2101

長女; 第一女

B0130_2102

次女

B0130_2103

三女

B0130_2104

四女

B0130_2105

五女

B0130_2106

六女

B0130_2107

七女

B0130_2108

八女

B0130_2109

九女

B0211_0000

조부모

B0211_1000

할아버지

B0211_2000

할머니

B0212_0000

외조부모

B0212_1000

외할아버지

B0212_2000

외할머니

B0214_0000

처조부모

B0214_1000

처조부

B0214_2000

처조모

B0213_0000

부조부모

B0213_1000

부조부

B0213_2000

부조모

B0220_0000

형제-자매

B0220_1000

(오빠)

B0220_2000

(오빠)의 처

B0220_2100

누나(언니)

B0200_1100

누나(언니의) 남편

B0220_1200

남동생

B0220_2200

남동생의 처

B0220_2300

여동생

B0220_1300

여동생의 남편

B0224_0000

처의 형제-자매

B0224_1000

처의 형(오빠)

B0224_2000

처의 형(오빠)의 처

B0224_2100

처의 누나(언니)

B0204_1100

처의 누나(언니의) 남편

B0224_1200

처의 남동생

B0224_2200

처의 남동생의 처

B0224_2300

처의 여동생

B0224_1300

처의 여동생의 남편

B0223_0000

남편의 형제-자매

B0223_1000

남편의 형(오빠)

B0223_2000

남편의 형(오빠)의 처

B0223_2100

남편의 누나(언니)

B0203_1100

남편의 누나(언니의) 남편

B0223_1200

남편의 남동생

B0223_2200

남편의 남동생의 처

B0223_2300

남편의 여동생

B0223_1300

남편의 여동생의 남편

B0230_0000

손자-손녀

B0230_1000

손자

B0230_2000

손자의 처

B0230_2100

손녀

B0230_1100

손녀의 남편

B0311_0000

증조부모

B0311_1000

증조부

B0311_2000

증조모

B0312_0000

외증조부

B0312_1000

외증조부

B0312_2000

외증조모

B0314_0000

처증조부모

B0314_1000

처증조부

B0314_2000

처증조모

B0313_0000

부증조부모

B0313_1000

부증조부

B0313_2000

부증조모

B0311_0100

삼촌-고모-

B0311_1100

삼촌

B0311_2100

삼촌의 처

B0311_1110

백부(큰아버지)

B0311_2110

백모(큰아버지의 처)

B0311_1120

숙부(작은아버지)

B0311_2120

숙모(작은아버지의 처)

B0311_2150

고모

B0311_1150

고모의 남편

B0311_2160

큰고모

B0311_1160

큰고모의 남편

B0311_2170

작은고모

B0311_1170

작은고모의 남편

B0312_0200

외숙-이모-

B0312_1200

외숙

B0312_2200

외숙의 처

B0312_2150

이모

B0312_1150

이모의 남편

B0314_0100

처고모부-처숙부-

B0314_1100

처고모부

B0314_2100

처고모부의 처

B0314_2150

처고모

B0314_1150

처고모의 남편

B0313_0100

부고모부-부숙부-

B0313_1100

부고모부

B0313_2100

부고모부의 처

B0313_2150

부고모

B0313_1150

부고모의 남편

B0331_0100

-생질-

B0331_1100

B0331_1200

질의 처

B0331_2150

생질

B0331_1150

생질의 처

B0411_0000

고조부모

B0411_1000

고조부

B0411_2000

고조모

B0412_0000

사촌

B0412_1000

남자사촌

B0412_2000

여자사촌

B0412_0100

종형제

B0412_1100

남자종형제

B0412_2100

여자종형제

B0412_0200

내종형제

B0412_1200

남자내종형제

B0412_2200

여자내종형제

B1001_1000

대부(大父)

B1001_1100

족숙(族叔)

B1002_1000

족형(族兄)

B1002_1100

족제(族弟)

B1003_1000

족질(族姪)

B9900_1000

종씨(宗氏), 존장(尊長)

 


 






+ XML 문서 설계

최대한 단순화 시켰다. 사실 복잡하게 하는게 더 이상하다. 

문제는 어떤 관계인지 판단해서 추가해주는 것이...일이지. ㅠㅠ 아우..ㅠㅠ




DTD

<?xml version="1.0" encoding="UTF-8"?>

<?generated by 바로-Baro-把路 ddokbaro.com  ddokbaro@gmail.com (2014.01.10)?>

<?Korea Historical Network DataBase?>

 

<!ELEMENT KHNDB ((인물+))>

<?우선 인물만 네트워킹 작업을 하고 추후 기관, 관직 등을 추가?>

 

<!ELEMENT 인물 ((관계정보))>

<!ATTLIST 인물   id CDATA #REQUIRED>

 

<!ELEMENT 관계정보 ((관계*))>

 

<!ELEMENT 관계 (#PCDATA)>

<!ATTLIST 관계   유형 CDATA #REQUIRED>

 

 

XML 예시

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE KHNDB SYSTEM "KHNDB.dtd">

<?generated by 바로-Baro-把路 ddokbaro.com  ddokbaro@gmail.com (2014.01.10)?>

<?Korea Historical Network DataBase?>

<?대상 인물 : 98140......어느 세월에 하나?>

 

<KHNDB>

             <인물 id="가귀U9999X0">

                           <관계정보>

                                        <관계 유형="S202">승전U9999X0</관계>

                           </관계정보>

             </인물>

             <인물 id="가루U9999X0">

                           <관계정보>

                                        <관계 유형="S404">안승U9999X0</관계>

                           </관계정보>

             </인물>

             <인물 id="가마U9999X0">

                           <관계정보>

                                        <관계 유형="S0401">아탁득문U9999X0</관계>

                                        <관계 유형="S0401">奇非U9999X9</관계>

                                        <관계 유형="S0401">欽明王U9999X9</관계>

                                        <관계 유형="S0401">眞慕宣文U9999X9</관계>

                           </관계정보>

             </인물>

             <인물 id="가실U9999X0">

                           <관계정보>

                                        <관계 유형="B0020_2000">설씨녀U9999X0</관계>

                           </관계정보>

             </인물>

             <인물 id="가실왕U9999X0">

                           <관계정보>

                                        <관계 유형="S0403">우륵U9999X0</관계>

                                        <관계 유형="S0111">우륵U9999X0</관계>

                                        <관계 유형="S0407">진흥왕U9999X9</관계>

                                        <관계 유형="S0603">진흥왕U9999X9</관계>

                           </관계정보>

             </인물>

             <인물 id="가안책B1559X0">

                           <관계정보>

                                        <관계 유형="B0110_1000">賈忠權U9999X9</관계>

                                        <관계 유형="B0220_1200">賈安術U9999X9</관계>

                           </관계정보>

             </인물>

             <인물 id="가안책B1559X0">

                           <관계정보>

                                        <관계 유형="S0404">부여융B0615X0</관계>

                                        <관계 유형="S0603">蘇定方U9999X9</관계>

                           </관계정보>

             </인물>

</KHNDB>

 

 

 

 

DTD(문서 형식 정의(Document Type Definition)는 컴퓨터 용어로, SGML 계열의 마크업 언어에서 문서 형식을 정의하는 것이다. SGML을 비롯해 HTML, XHTML, XML 따위에서 쓰인다. XML DTD는 XML 문서의 구조를 정의한다. 다시 말해서 Valid XML 문서가 되기 위해 문서 내에 어떤 요소를 사용하고, 요소간의 계층구조를 기술하고, 요소의 내용으로 올 수 있는 내용을 정의한다.


DTD는 문법이 간단하며, SGML 계열에서 사용되어 왔기에 기존의 다양한 문서 정의가 DTD로 되어 있다. 그러나 DTD가 가지고 있는 XML에서의 다양한 확장의 한계로 인하여 XML_Schema 표준을 사용할 것을 추천한다.



1. DTD 선언 방식

1.1. 외부 DTD 선언

<?xml version="1.0" ending="utf-8"?>

<!DOCTYPE 루트요소이름 SYSTEM "DTD문서경로">


SYSTEM : 시스템에 존재하는 물리적인 위치정보. 파일이름 or 특정URL

<!DOCTYPE CONCEPT SYSTEM "concept.dtd">

<!DOCTYPE CONCEPT SYSTEM "http://ddokbaro.tistory.com/xml/concept.dtd">

<!DOCTYPE CONCEPT SYSTEM "file:///c:/concept.dtd">


PUBLIC : 특수한 형태를 식별자를 사용하여 외부DTD 참조

<!DOCTYPE member PUBLIC "-//W3C/DTD XHTML 1.0 Strict//EN" >


혼합방식 : SYSTEM  + PUBLIC 

<!DOCTYPE CONCEPT PUBLIC "-//W3C/DTD XHTML 1.0 Strict//EN" "http://ddokbaro.tistory.com/xml/concept.dtd" >


1.2. 내부 DTD 선언 - 실무에서 쓸일 없음.

1.3. 외부-내부 DTD 선언 - 실무에서 쓸일 없음. 




2. ELEMENT

2.1. ELEMENT 정의 형태

#PCDATA(문자 데이터) : <!ELEMENT ele-name (#PCDATA)>

자식요소 : <!ELEMENT ele-name (ele-name+, ele-name, ....)+>

자식요소와 문자 데이터의 혼합 : <!ELEMENT ele-name (#PCDATA | ele-name ....)*>

EMPTY(빈 content) : <!ELEMENT ele-name EMPTY>

ANY(위의 4가지가 모두 오는 경우) : <!ELEMENT ele-name ANY>


2.2. 출연횟수 정의


?

 0 or 1 (출현하지 않거나 1번만 출현)

 0개 이상 출현(출현하지 않거나 반복 출현가능)

+

 1개 이상 출현(무조건 1번은 출현해야되고, 여러번 반복 출현 가능)


2.3. #PCDATA(문자 데이터)

- PCDATA : Parsed Character Data 

- 출현횟수 정의 불가 : <!ELEMENT name (#PCDATA)+>  (X)


2.4. 자식요소

<!ELEMENT CONCEPT (time, place)> 

<!ELEMENT PEOPLE ( (Mr|Ms), Firstname, Middlename*, Lastname) >

<!ELEMENT PEOPLE ( email*, ( homePhone | officePhone | mobilePhone)+, address?) > 


2.5. #PCDATA + 자식요소

<!ELEMENT 요소이름 (#PCDATA|요소명1|요소명2.....)*>

#PCDATA는 반드시 자식요소보다 먼저 와야 한다.

- "|" 이외의 출현횟수나 출현방법은 괄호 밖에 정의한다.


2.6. EMPTY - 빈요소, 실무에서 사용빈도 적음

<!ELEMENT 요소명 EMPTY>


2.7. ANY - 모든사항 허용, 최대한 사용하지 말 것

<!ELEMENT 요소명 ANY>




3. Attribute

3.1. 기본형식

<!ATTLIST ele-name (att-name type default-decl)+>


ele-name : 속성이 포함될 요소의 이름

att-name : 요소 안에 포함될 속성의 이름

type : 속성값을 제한하기 위한 값으로 10가지 속성타입이 올 수 있다.

default-decl : 속성이 요소에 반드시 포함되어야 하는지 등의 특별한 제한을 두기 위하여 사용된다. => #IMPLIED, #REQUIRED, 기본값, #FIXED


3.2. att-name

- 문자 시작, 숫자 시작 불가, 공백불가, "_", ":"로 시작가능


3.3. type

 CDATA 

 문자데이터(단순한 텍스트 문자열) 

 열거형 

 속성값은 반드시 DTD에 명시적으로 정의된 일련의 값들 중의 하나가 되어야 한다. 

 ID 

 속성값은 해당 요소에서 고유한 식별자이다. 이것은 반드시 모든 XML 이름 규칙을 따르는 텍스트 문자열 이어야 한다.

 IDREF 

 IDREF의 값과 똑같은 ID 값을 갖고 있는 요소에 대한 참조 값 

 IDREFS 

 공백에 의해 경계가 나뉘는 IDREF들의 리스트 

 NMTOKEN 

 하나의 이름 토큰 - XML 이름 규칙을 따르는 텍스트 문자열 

 NMTOKENS 

 공백에 의해 경계가 나뉘는 NMTOKEN들의 리스트 

 ENTITY

 이미 정의된 ENTITY의 이름 

 ENTITIES 

 공백에 의해 경계가 나뉘는 ENTITY 이름들의 리스트 

 NOTATION

 속성값은 DTD에 명시적으로 선언된 NOTATION type이 되어야 한다. 


<!ATTLIST ele-name att-name CDATA default-decl >

<!ATTLIST elel-name att-name (value1 | value2 | .. ) default-decl >

<!ATTLIST ele-name att-name ID (#REQUIRED|#IMPLIED)>

<!ATTLIST ele-name att-name IDREF(S) default-decl>

<!ATTLIST ele-name att-name NMTOKEN(S) default-decl >



3.4. default-decl

#REQUIRED

 속성은 반드시 요소에서 사용되어야 한다. 

#IMPLIED   속성 사용이 선택적이며, 만일 사용하지 않으면 요소는 이 속성을 갖지 않는다. 기본 값은 줄 수없다. 

 #FIXED

 속성 사용은 선택적이고, 사용하지 않으면 고정 값을 기본 값으로 가지며 사용할 경우, "고정 값"을 속성 값으로 가진다. 즉 상수와 동일하다. 




4. ENTITY

- 반복적으로 사용되는 문장이나 문자열을 참조

XML조각, DTD조각, 긴 문자열, 상수, non-XML 데이터 사용가능

- 세부사항 참조 링크




5. NOTATION - 실무에서는 추천 안함

- XML 응용프로그램에서 사용할 외부 응용 프로그램이나 이진 데이터에 이름 부여

<!NOTATION not-name SYSTEM URI>

<!NOTATION not-name  PUBLIC pURI sURI>



오픈과 저작권은 서로 대립하는 존재가 아니다. 오히려 저작권의 존재로 인하여 오픈이 날개를 달 수 있다. 공급자는 자신이 원하는 범위의 저작권을 제공하고, 사용자들은 공급자가 제공하는 저작권 범위에서 자유롭게 사용할 수 있다. 



+ 데이터 관련 - 크리에이티브 커먼즈(Creative Commons, CC)

크리에이티브 커먼즈(Creative Commons, CC)은 사실상 전세계 데이터에 대한 저작권의 표준으로서 작용하고 있다. 공급자는 원저작자 표기(필수), 영리/비영리, 변경허용/금지, 동일조건 변경 허용/금지 중에서 선택하여 콘텐츠를 제공하며, 사용자는 공급자의 CC 표기에 부합하는 범위에서 자유롭게 데이터를 이용할 수 있다.


현재 대한민국법이 적용된 버젼은 "CC 2.0 대한민국"이다. 사단법인 한국정보법학회에서 대한민국에 맞춘 지역화를 담당하고 있다. 


- 관련 사항 

크리에이티브 커먼즈 http://ko.wikipedia.org/wiki/크리에이티브_커먼즈

크리에이티브 커먼즈 코리아 http://www.cckorea.org

CC 설명 http://cckorea.org/xe/?mid=ccl

CC 세부기술문서(한국어) http://www.cckorea.org/ccrel-kr.html (EUC-KR)

한국정보법학회 http://kafil.or.kr/




+ SW 관련 - GPL LGPL BSD MPL EPL QPL


 

GPL 

LGPL 

BSD 

MPL 

EPL 

QPL 

상용QT 

 무료이용

 가능

불가

 자유배포

 가능

불가

 코드공개

 공개

비공개 

공개 

비공개 

 코드수정

가능

 수정코드공개

 공개

비공개 

공개 

비공개 

 상용SW연계

불가 

가능 

불가 

가능 



출처 : http://freshmeat.net/stats/#license, 2008년 10월 16일


현재 디지털 인문학의 영역은 대부분이 학술적인 부분에서 정부기금으로 운영되고 있기에 가장 엄격하면서 많이 사용되는 오픈SW 라이선스인 GPL을 사용하거나 적용하는 것이 자유롭다. 이런 경향성은 디지털 인문학의 내재적인 성격상 지속적으로 유지될 것으로 보인다. 


- 관련 사항

오픈소스 라이센스 위키

주요오픈소스라이센스 정리(블로그) 

공개SW포털 https://www.oss.kr

오픈소스 SW 라이센스 가이드(KLDP) 

오픈소스 소프트웨어 라이센스의 이해와 활용 (상) 

오픈소스 소프트웨어 라이센스의 이해와 활용 (하) 


정부 3.0을 비롯한 오픈데이터 정책에 따라서 현재 다양한 데이터들이 공개되고 있다. 그런데 어떤 형식과 기술이 제공자와 사용자를 만족시킬 수 있는 방법일까? 간략하게 생각해보도록 하겠다. 


* 지속적으로 업데이트 처리하고 있습니다. 관련 사항에 대한 의견 부탁드려요^^



1. 원데이터 제공(RAW DATA) 

공급자가 데이터가 최초에 생산된 형태(XML, HWP, 엑셀 등)로 사용자에게 콘텐츠를 제공하는 것을 의미한다. 공급자는 별다른 투자 없이 직접적으로 사용자에게 데이터를 제공할 수 있고, 사용자는 원데이터에 직접적으로 접근하여 활용할 수 있는 장점이 있다. 그러나 원데이터의 양이 방대할 경우 사용자가 원하는 데이터를 찾아내기 어렵고, 사용자에게 데이터활용 능력이 부족할 경우 데이터를 활용한 재창조가 제한적이다. 또한 원데이터에 개인정보가 존재할 경우 개인정보보호에 대한 문제가 발생할 여지가 있다. 


원데이터 제공 방식은 전체 데이터의 제공방식으로는 부적합하다. 다만 전체 데이터의 일부분에 대한 검색서비스를 제공하는 조건 하에서 사용자에게 부가적인 서비스로서 원데이터 제공이 의미를 가진다고 할 수 있다. 


다만 원데이터는 차후 활용성을 생각하면 XML 방식의 데이터 형식을 사용 할 것을 권장한다. 




2. RDB 직접 접속

공급자가 원데이터를 이용하여 구축한 RDB에 사용자가 직접적으로 접속할 수 있게 하는 방식이다. 공급자는 별다른 투자 없이 직접적으로 사용자에게 데이터를 제공할 수 있고, 사용자는 정제된 데이터베이스를 쉽게 활용할 수 있다. 그러나 사용자에게 RDB에 대한 직접적인 접속을 제공함으로서 DDOS 공격 등의 보안상의 문제가 발생할 가능성이 매우 높다. 또한 원데이터에 개인정보가 존재할 경우 개인정보보호에 대한 문제가 발생할 여지가 있다. 


RDB 직접 접속은 특히 보안상의 문제로 인하여 일반적인 서비스로 제공할 수 없다. 다만 상호간의 신뢰관계가 구축된 기관 및 개인 간의 "상호데이터제공협정"을 통해서 RDB 직접접속 방식의 데이터 제공방식을 사용할 수 있다. 




3. 공개 API(Open Application Programmer Interface, Open API, 오픈 API)

공개 API(Open Application Programmer Interface, Open API, 오픈 API)는 누구나 사용할 수 있도록 공개된 API를 말한다. 웹 2.0의 핵심 기술의 하나로서 공급자가 특정 콘텐츠나 기술을 누구나 사용할 수 있도록 하는 기술을 의미한다. 대표적인 예로는 구글맵이 있으며, 전세계의 사용자들이 구글맵을 이용하여 다양한 콘텐츠와 "매쉬업"하여 새로운 서비스를 창조하고 있다. 공개 API는 공급자에게는 사용자에게 제공하는 콘텐츠의 내용을 제어할 수 있으며, 사용자는 정제된 데이터를 간단한 방식으로 제공받음으로서 다양한 활용을 시도할 수 있다. 


공개 API는 현재 실무에서 가장 합당한 데이터 제공방식이다. 공급자는 보안 문제 없이 자신이 원하는 범위에서 콘텐츠를 공유할 수 있고, 사용자는 간단한 방식으로 데이터를 제공 받아서 활용할 수 있다. 


관련 추가항목 : 

공개 API http://en.wikipedia.org/wiki/Open_API

매쉬업 http://en.wikipedia.org/wiki/Mashup_(web_application_hybrid)

JSON  http://en.wikipedia.org/wiki/JSON

REST http://en.wikipedia.org/wiki/Representational_state_transfer

OAuth http://en.wikipedia.org/wiki/OAuth




4. 링크드 데이터(linked data)

링크드 데이터(linked data)는 웹 상에 존재하는 데이터를 개별 URI(Uniform Resource Identifier)로 식별하고, 각 URI에 링크 정보를 부여함으로써 상호 연결된 웹을 지향하는 모형이다. 단일 사용자가 제공하는 데이터 뿐만이 아니라, 해당 데이터와 연계된 모든 관련 데이터를 사용자가 획득할 수 있기에 가장 이상적인 데이터 공유 방식이라고 할 수 있다. 그러나 링크드 데이터을 위해서는 공급자"들"이 현존하는 모든 원데이터를 특정 표준형식에 맞추어서 상호간 연결을 할 수 있도록 기존의 데이터에 대한 재처리가 필요하다.


가장 이상적인 데이터 공유방식이라고 할 수 있다. 그러나 기본적으로 기존 데이터에 대한 재처리 작업의 필요성이나 전세계적 표준형식의 부재 등으로 인하여 실무에서의 활용은 아직 제한적이라고 할 수 있다. 다만 미래를 대비하여 지금부터 만들어지는 원데이터에 대해서는 더블린코어나 유로피아나와 같은 비교적 활성화된 표준방식을 응용하는 것을 추천한다.



관련 추가항목 : 


MSSQL에서 XML 데이터를 운용하는 레퍼런스를 간략하게나마 정리해보았다[각주:1].


특히 상업적인 수요가 거의 없는 "인문학데이터를 XML을 기반으로 만들어서 RDB에서 사용"하기 위한 방법론 요약정리이다.



+ XML 기반 관계형 데이터베이스 설계의 3원칙[각주:2]

 1. 단위 노드의 XML 문서는 더 이상 분해하지 않고, 하나의 XML 컬럼에 적재한다.

단위 노드는 일반적으로 고유한 ID 값을 가지는 항목이면서 독립적인 관리의 대상이다. 


예를 들어서 종이사전의 하나하나의 명사("계절", "가구" 등)가 하나의 "단위 노드"가 된다. 물론 A로 시작하는 모든 명사("air, att....")를 "단위 노드"로 설정해도 무방하나, 실무에서 원고위탁등의 행위을 할 때 일반적으로 하나의 명사 단위로 관리를 하기 때문에 하나하나의 명사가 "단위 노드"가 되는 것이 관리하기 편하다.


 2. 주요 메타데이터 요소는 별도 테이블의 독립 컬럼 데이터로  관리한다.

일반적으로 자주 쓰이는 메타데이터 요소(ID, 이름 등)은 작업의 편이성을 위해서 독립적인 테이블로 만드는 것이 편하다. 또한 특정 서비스(갤러리, 지리정보서비스)의 구현을 위해서 필요한 데이터만을 추출하여 독립적인 테이블로 만드는 것도 작업의 편이성을 보장한다. 


다만 3번 항목을 충족시키기 위하여 View 테이블을 사용하는 것을 강력하게 권장한다.


 3. XML 본문 컬럼과 메타데이터 컬럼의 데이터는 항상 일치되도록 한다. 

XML 데이터에서 자주 사용되는 데이터를 View 테이블이 아닌 일반 테이블로 만들 경우 XML 데이터와 서비스되는 데이터 간의 상이성이 발생한다. 데이터 무결성은 DB 관리의 기본이지만, 기본이 지켜지지 않는 경우가 왕왕 발생하기에 다시 한번 강조된다. 


정말 왠만하면 View 테이블을 사용하라. 물론 View가 아닌 트리거를 사용할 수도 있다. 그러나 시스템부하나 일반적인 서비스환경을 생각하면 역시 View 기능을 사용하는 것이 비교적 합리적이다.




+ XML 데이터 입력

INSERT INTO  (테이블명)((컬럼명)) VALUES(

CONVERT(xml, -- XML이라고 명시 

N' -- 유니코드 명시

(XML 데이터)

', 1) -- data_type 설정(data_type 참고)

)


* CONVERT xml data_type(http://msdn.microsoft.com/ko-kr/library/ms187928.aspx)

0(기본값) : 불필요한 공백을 삭제하고 내부 DTD 하위 집합을 허용하지 않는 기본 구문 분석 동작을 사용합니다.

1 : 불필요한 공백을 유지합니다. 이 스타일 설정에 따라 xml:space="preserve"가 대신 지정된 경우와 동일하게 동작하도록 기본 xml:space 처리가 설정됩니다.

2 : 제한된 내부 DTD 하위 집합 처리를 설정합니다

3 : 불필요한 공백을 유지하고 제한된 내부 DTD 하위 집합 처리를 설정합니다.



+ XML 스키마 컬렉션

--스키마 만들기

CREATE XML SCHEMA COLLECTION 스키마명 AS '

XML스키마내용

'

-- 스키마 사용

CREATE TABLE 테이블명 (

              컬럼명 속성값 IDENTITY PRIMARY KEY,

              컬럼명 xml 스키마명 -- XML 스키마 컬렉션 지정

)


+ XML 데이터 조작 메소드

query() : XML 인스턴스에 대해 쿼리 : 검색된 XML 인스턴스 

value() : XML 인스턴스에서 값을 검색 : 검색된 XML 인스턴스의 값 

exist() : NULL이 아닌 결과를 반환하는지 여부를 확인 : TRUE/FALSE 

modify() : XML 데이터의 업데이트를 수행하도록 DML 문을 지정 : 반환값 없음 

nodes() : 여러개의 XML 인스턴스를 여러개의 행으로 반환 :노드별로 추출된 XML 인스턴스를 담은 행 집합(테이블) 


1. 기본적인 query()

select 칼럼명.query('/') -- 루트노드의 XML값 반환

select 칼럼명.query('/노드A') -- 자식노드 A의 XML값 반환

select 칼럼명.query('/노드A/노드B') -- 자식노드 B의 XML값 반환

select 칼럼명.query('/노드A/@속성B') -- 자식노드 A의 속성 B의 XML값 반환


2. 상대경로 지정 query()

select 칼럼명.query('/노드A/노드B/.') -- 자식노드 B 자체 XML값 반환

select 칼럼명.query('/노드A/노드B/..') -- 자식노드 B의 상위노드(A노드)의 XML값 반환


3. 재귀적내림 연산자 query()

select 칼럼명.query('//노드A') -- 모든 A 노드의 XML값 반환

select 칼럼명.query('//노드A[1]') -- 문서내 1번째 A 노드의 XML값 반환

select 칼럼명.query('//노드A[2]') -- 문서내 2번째 A 노드의 XML값 반환

select 칼럼명.query('//@속성A') -- 문서내 모든 속성A의 XML값 반환


4. 필터 사용 query()

select 칼럼명.query('//노드A[노드B]') -- 노드 B를 포함한 모든 노드 A의 XML값 반환

select 칼럼명.query('//노드A[@속성B]') -- 속성 B를 포함한 모든 노드 A의 XML값 반환

select 칼럼명.query('/노드A/노드B[노드C]') -- 노드 C를 포함한 노드 A의 자식노드 B의 XML값 반환

select 칼럼명.query('//노드A[.="키워드B"]') -- 노드값이 B인 모든 노드 A의 XML값 반환

select 칼럼명.query('//노드A[@속성B="키워드C"]') -- 속성 B의 값이 C인 모든 노드 A의 XML값 반환

select 칼럼명.query('//노드A[노드B="키워드C"]') -- 노드B의 값이 C인 모든 노드 A의 XML값 반환


5. Xpath 함수사용 query()

select 칼럼명.query('/노드A/node()') -- 노드 A의 XML값을 반환

select 칼럼명.query('/노드A/text()') -- 노드 A의 모든 하위 노드를 제외한 XML값을 반환

select 칼럼명.query('/노드A[last()]') -- 마지막 노드 A의 XML 값을 반환

select 칼럼명.query('count(/노드A)') -- 노드 A의 출현횟수를 count한 XML 값을 반환

select 칼럼명.query('sum(노드A/@속성B)') -- 노드 A의 모든 속성B을 sum 한 XML 값을 반환


6. 기본적인 value() 

select 칼럼명.value('(/노드A)[1]', 'nvarchar(100)') -- 1번째 자식노드 A의 값을 nvarchar(100) 형식으로 반환

select 칼럼명.value('(/노드A)[1]', 'real') -- 1번째 자식노드 A의 값을 real형식으로 반환

select 칼럼명.value('(/@속성A)[1]', 'real') -- 1번째 속성 A의 값을 real형식으로 반환



7. 여러개의 값 반환 value() + nodes()

SELECT 지정노드_임시명.value('/노드B', 'nvarchar(100)') 

-- "지정노드_임시명"의 노드A의 위치의 모든 노드 B의 값을 nvarchar(100) 형식으로 반환

FROM  테이블명 CROSS APPLY 칼럼명.nodes('/노드A') AS R(지정노드_임시명) 

- 노드A의 위치를 "지정노드_임시명"으로 저장


* 추천

text데이터(한글+한자) = nvarchar

geo(위경도)데이터 = real



8. 기본적인 exist()

-- exist()는 where 조건절에 쓰여서 특정값을 가져올 때 쓰인다

SELECT 칼럼명 FROM DB명 WHERE 칼럼명.exist('노드A') = (0 or 1)


9. 기본적인 modify()

9.1. replace

UPDATE DB명 

SET 칼럼명.modify('replace value of (노드A())[1] with "입력값" ') 

WHERE 조건값


9.2. insert

UPDATE DB명 

SET 칼럼명.modify ('insert 입력값 after (노드A)[1]') 

WHERE 조건값


9.3. delete

UPDATE DB명 

SET 칼럼명.modify('delete 대상위치값[1]') 

WHERE 조건값



value()도 더 자세히 해야겠고 exist()나 modify()도 넣어야겠네.....나중에...언젠가?!





+ 실무작업 예시[각주:3]

 

-- [Create Table] 기본테이블 세팅

create table jeju_xml (

id nvarchar(10) NOT NULL, -- ID 컬럼, 기본키

xmltxt xml NOT NULL, -- XML 항목 2G까지 입력가능.

primary key(id)

)

 

-- [Trigger] XML 데이터를 입력했을 때 해당 XML 데이터에서 자동으로 ID값 추출

CREATE TRIGGER jeju_getid ON jeju_xml INSTEAD OF INSERT

AS

INSERT INTO jeju_xml(id, xmltxt)

SELECT

t.xmltxt.value('(/항목/@ID)[1]', 'nvarchar(10)') AS id,

t.xmltxt AS xmltxt

FROM inserted t

 

-- [Data Input] 예시데이터 입력

INSERT INTO jeju_xml(xmltxt) VALUES(

CONVERT(xml,

N'<항목 ID="2011-5-01">

<항목명>용연(龍淵)</항목명>

<메타데이터>

<일시>2011. 10. 10. 09:30</일시>

<주소>제주특별자치도 제주시 용담동</주소>

<위치>

<경도>126.5144225</경도>

<위도>33.51488194</위도>

<고도>26.1</고도>

</위치>

</메타데이터>

<본문>

첫 방문지. <지명>용연(龍淵)</지명><지명>한천(漢川)</지명>이라는 이름의 하천이 바다와 만나는 지점. 양안이 절벽인 하천 하구에 바닷물이 들어 연못처럼 보인다. 옛날에는 <지명>제주</지명>에 부임한 관리들이 달밤에 이곳에서 배를 띄우고 놀았다고.... 지금은 연인들이 즐겨 찾는 여행 코스인 듯. 다리 난간 와이어에 이른바 사랑의 자물쇠를 채우는 일이 빈번해지자 시에서는 별도의 자물쇠 걸이를 설치하였다.

<삽도 url="L1080486m.jpg">용연(龍淵) 안내판</삽도>

<삽도 url="L1080477m.jpg">용연(龍淵)</삽도>

<삽도 url="IMG_9073.jpg">용연(龍淵)</삽도>

<삽도 url="IMG_9077.JPG">용연(龍淵) 석벽에는 목사 심원택이라 새겨져 있다.</삽도>

<삽도 url="L1080485m.jpg">시에서 설치한 자물쇠 걸이.</삽도>

</본문>

</항목>', 1)

)

 

select * from jeju_xml -- 입력 확인용

 

-- [Meta View] 기본정보 제공을 위한 view 테이블 생성

create view jeju_meta as

select

id,

xmltxt.value( '(/항목/항목명)[1]', 'nchar(80)' ) as 항목명,

xmltxt.value( '(/항목/메타데이터/일시)[1]', 'nchar(20)' ) as 일시,

xmltxt.value( '(/항목/메타데이터/주소)[1]', 'nchar(40)' ) as 주소

from jeju_xml

 

select * from jeju_meta -- 추출내용 확인용

 

 

-- [GIS View] GIS 서비스를 위한 view 테이블 생성

create view jeju_gis as

select

id,

xmltxt.value( '(/항목/메타데이터/위치/경도)[1]', 'real' ) as 경도,

xmltxt.value( '(/항목/메타데이터/위치/위도)[1]', 'real' ) as 위도,

xmltxt.value( '(/항목/메타데이터/위치/고도)[1]', 'real' ) as 고도

from jeju_xml

 

select * from jeju_gis -- 추출내용 확인용

 

 

-- [Photo View] 갤러리 서비스를 위한 view 테이블 생성

create view jeju_photo as

SELECT id as txt_id,

photo.value('./@url', 'nchar(40)' ) as 파일,

photo.value('.', 'nchar(100)' ) as 캡션

FROM jeju_xml CROSS APPLY xmltxt.nodes('/항목/본문/삽도') AS R(photo)

 

select * from jeju_photo -- 추출내용 확인용

 

-- [Keyword Index] 인덱스 서비스를 위한 인덱스 생성

-- 지명추출

create view jeju_place as

SELECT name.value('.', 'nchar(40)' ) as 지명, id as txt_id

FROM jeju_xml CROSS APPLY xmltxt.nodes('/항목/본문/지명') AS R(name)


-- 인덱스 추출 

create view jeju_pndx as

select distinct 지명, count(*) as 빈도 from jeju_place group by 지명

 

select * from jeju_pndx order by 지명 -- 인덱스 정보 확인용



  1. 사실 결정적인 이유는...본인이 매일매일 혼동을 해서......-0-;;; [본문으로]
  2. 김현교수님 제정-0-;; [본문으로]
  3. 본 내용은 김현교수님의 수업내용에서 추출했음. [본문으로]





오픈API 툴 및 가이드 페이지


한국정보화진흥원은 공공기관의 데이터 개방을 촉진하고 안정적인 데이터 제공 확대를 위해 "오픈 API 개발 자동화 도구"을 보급한다. Open API을 만들기 위해 매번 반복하는 코딩작업을 없애고 웹기반 UI을 통해서 간단하게 제작할 수 있는 툴이다. 



바로 : 구체적인 내용은 실제로 사용해보고 나중에 올리도록 하........겠지?? -0-;; 다만 대락적으로 메뉴얼을 살펴보면 상당히 괜찮은 아이디어이며 버그가 없다면 괜찮을듯한데....사실 직접 사용해보고 최종판단을 내려야되서....



+ Recent posts