블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2328)N
Unity3D (572)N
Programming (472)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (34)
3DS Max (3)
Game (12)
Utility (116)
Etc (92)
Link (31)
Portfolio (19)
Subject (90)
iOS,OSX (38)
Android (13)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (1)
Memories (19)
Interest (37)
Thinking (36)
한글 (26)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (19)
Exercise (3)
나만의 맛집 (2)
냥이 (9)
육아 (9)
Total1,346,328
Today134
Yesterday132
Statistics Graph

달력

« » 2019.12
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        

공지사항

태그목록


Link : http://forum.codecall.net/topic/71788-reading-excel-files-in-c/

'Programming > C#' 카테고리의 다른 글

다른 프로세스가 사용중이라면서 에러가 나는경우에 이렇게  (2) 2014.03.17
제네릭(Generic)과 제약조건  (0) 2014.03.13
Reading Excel Files in C#  (0) 2014.03.12
C# 의 Shift 비트 연산 정리  (0) 2013.12.18
Copy List to List  (0) 2013.10.10
Sorting  (0) 2013.10.02
Posted by blueasa

댓글을 달아 주세요

우선, 모델이 되는 숫자 하나를 골라볼까요? ^^

uint un = 2320987651;
int n = 2020987651;
OutputBitText(un + "[unsigned] ==> \t", un);
OutputBitText(n + "[signed]   ==> \t", n);

private static void OutputBitText(string text, uint n)
{
    Console.WriteLine(text + " " + Convert.ToString(n, 2).PadLeft(32, '0'));
}

private static void OutputBitText(string text, int n)
{
    Console.WriteLine(text + " " + Convert.ToString(n, 2).PadLeft(32, '0'));
}

// 출력 결과
2320987651[unsigned] ==>         10001010010101110111011000000011, 2320987651
-1753885949[signed]   ==>        10010111011101011101001100000011, -1753885949

여기서 기본적인 C# 의 Bit Shift 연산자 (<<, >>) 를 사용해 보겠습니다.

2320987651[unsigned] ==>         10001010010101110111011000000011, 2320987651
[unsigned] >> 4 ==>              00001000101001010111011101100000, 145061728
[unsigned] << 4 ==>              10100101011101110110000000110000, 2776064048

-1753885949[signed]   ==>        10010111011101011101001100000011, -1753885949
[signed]   >> 4 ==>              11111001011101110101110100110000, -109617872
[signed]   << 4 ==>              01110111010111010011000000110000, 2002595888

보시는 것처럼, C# 의 기본 Bit Shift 연산자는 다음과 같은 규칙이 있습니다.

unsigned 의 경우, 밀려난 비트 들에 대한 처리를 하지 않고 최상위 비트에 대한 처리도 없음
signed 의 경우, 
    우측 shift 연산자는 최상위 부호 비트를 유지하면서 밀려난 비트들에 대한 처리를 하지 않고,
    좌측 shift 연산자는 부호 비트를 지키지 않고, 역시 밀려난 비트들에 대한 처리를 하지 않는다.

자, 그럼 여기서 부호 비트는 상관없이 순수하게 rotation 식의 shift 연산을 수행하려면 어떻게 해야 할까요? 이를 위해서는 다음과 같은 보조 함수를 만들어줘야 합니다.

C# bitwise rotate left and rotate right
; http://stackoverflow.com/questions/812022/c-sharp-bitwise-rotate-left-and-rotate-right

static uint UnsignedLeftShift(uint number, int shift)
{
    return (uint)((number << shift) | (number >> (32 - shift)));
}

static uint UnsignedRightShift(uint number, int shift)
{
    return (uint)((number >> shift) | (number << (32 - shift)));
}

사용법은 unsigned 의 경우 그냥 사용하면 되지만, signed 의 경우에는 unsigned 형변환을 해줘야만 정상적으로 rotation 이 됩니다.

OutputBitText("[rotation] >> 4 ==> \t\t", UnsignedRightShift(un, 4));
OutputBitText("[rotation] << 4 ==> \t\t", UnsignedLeftShift(un, 4));

OutputBitText("[rotation] >> 4 ==> \t\t", UnsignedRightShift((uint)n, 4));
OutputBitText("[rotation] << 4 ==> \t\t", UnsignedLeftShift((uint)n, 4));

// 출력 결과
2320987651[unsigned] ==>         10001010010101110111011000000011, 2320987651
[rotation] >> 4 ==>              00111000101001010111011101100000, 950368096
[rotation] << 4 ==>              10100101011101110110000000111000, 2776064056

-1753885949[signed]   ==>        10010111011101011101001100000011, -1753885949
[rotation] >> 4 ==>              00111001011101110101110100110000, 964123952
[rotation] << 4 ==>              01110111010111010011000000111001, 2002595897

마지막으로 자바의 unsigned right shift 연산자(>>>) 에 해당하는 C# 의 코드는 어떻게 구현해야 할까요?

비교를 위해 우선 자바의 비트 처리 값을 확인해 보면 다음과 같은 데요.

==== Java ====
   n       == 320987651  == 00010011001000011110001000000011
  -n       == -320987651 == 11101100110111100001110111111101

   n >>> 3 == 40123456   == 00000010011001000011110001000000 
  -n >>> 3 == 496747455  == 00011101100110111100001110111111

즉, 최상위 비트를 무시하고 무조건 shift 연산만을 수행하는 것입니다. 이에 대한 C# 의 동일한 작업은 다음의 글에서 소개하는 것처럼 간단합니다.

bitwise unsigned right shift >>>
; http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/6c892d10-75ff-4f4e-a555-3017f72ec170/

static int TripleRightShift(int number, int shift)
{
    return (int)((uint)number >> shift);
}

// 출력 결과
320987651  >>> 3 ==  00000010011001000011110001000000, 40123456
-320987651 >>> 3 ==  00011101100110111100001110111111, 496747455

보시는 것처럼, 자바의 >>> 연산자와 동일한 출력 결과를 보입니다.

그럼... 정리가 다 된 것 같군요. ^^

(첨부된 파일은 위의 코드를 포함한 예제 프로젝트입니다.)

부가적으로 다음의 글도 예전에 쓴 적이 있답니다. ^^

C# - Right operand 가 음수인 Shift 연산 결과
; http://www.sysnet.pe.kr/2/0/1008




[출처] C# 의 Shift 비트 연산 정리|작성자 techshare


'Programming > C#' 카테고리의 다른 글

제네릭(Generic)과 제약조건  (0) 2014.03.13
Reading Excel Files in C#  (0) 2014.03.12
C# 의 Shift 비트 연산 정리  (0) 2013.12.18
Copy List to List  (0) 2013.10.10
Sorting  (0) 2013.10.02
C# 에서 콘솔 프로그램을 숨기는 방법 ( Using ProcessStartInfo Class )  (0) 2013.07.23
Posted by blueasa

댓글을 달아 주세요

Copy List to List

Programming/C# / 2013. 10. 10. 15:22

For a list of elements

List<string> lstTest = new List<string>();
                lstTest.Add("test1");
                lstTest.Add("test2");
                lstTest.Add("test3");
                lstTest.Add("test4");
                lstTest.Add("test5");
                lstTest.Add("test6");

If you want to copy all the elements

lstNew.AddRange(lstTest);

If you want to copy the first 4 elements

List<string> lstNew = new List<string>();
lstNew = lstTest.GetRange(0, 4);



출처 : http://stackoverflow.com/questions/1952185/how-do-i-copy-items-from-list-to-list-without-foreach

'Programming > C#' 카테고리의 다른 글

Reading Excel Files in C#  (0) 2014.03.12
C# 의 Shift 비트 연산 정리  (0) 2013.12.18
Copy List to List  (0) 2013.10.10
Sorting  (0) 2013.10.02
C# 에서 콘솔 프로그램을 숨기는 방법 ( Using ProcessStartInfo Class )  (0) 2013.07.23
Connection strings for Excel(OLEDB/ODBC)  (0) 2013.07.03
Posted by blueasa

댓글을 달아 주세요

Sorting

Programming/C# / 2013. 10. 2. 17:46


링크 : http://support.microsoft.com/kb/320727/ko

링크 : http://www.codeproject.com/Articles/42839/Sorting-Lists-using-IComparable-and-IComparer-Inte

링크 : http://www.dotnetperls.com/sort-string-array

Posted by blueasa

댓글을 달아 주세요

1 System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); 2 psi.FileName = "FilePath"; // File Path 3 psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 4 psi.CreateNoWindow = true; // hidden 을 시키기 위해서 이 속성도 true 로 체크해야 함 5 psi.Arguments = "parameters"; // Parameters. if you using 6 7 System.Diagnostics.Process.Start(psi);



참조 : http://mynotepad.tistory.com/120

Posted by blueasa

댓글을 달아 주세요

원본 : http://www.connectionstrings.com/excel



Connection strings for Excel

Developers Community

Find solutions and post questions regarding connection string related issues.

Forum for Excel

Microsoft Jet OLE DB 4.0

TYPE OLE DB Provider
USAGE Provider=Microsoft.Jet.OLEDB.4.0
MANUFACTURER Microsoft

Standard

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;
Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";
 

Standard alternative

Try this one if the one above is not working. Some reports that Excel 2003 need the exta OLEDB; section in the beginning of the string.

OLEDB;Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyExcel.xls;
Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";

Important note!
The quota " in the string needs to be escaped using your language specific escape syntax.
c#, c++   \"
VB6, VBScript   ""
xml (web.config etc)   &quot;
or maybe use a single quota '.

"HDR=Yes;" indicates that the first row contains columnnames, not data. "HDR=No;" indicates the opposite.

"IMEX=1;" tells the driver to always read "intermixed" (numbers, dates, strings etc) data columns as text. Note that this option might affect excel sheet write access negative.

SQL syntax "SELECT [Column Name One], [Column Name Two] FROM [Sheet One$]". I.e. excel worksheet name followed by a "$" and wrapped in "[" "]" brackets.

Check out the [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel] located registry REG_DWORD "TypeGuessRows". That's the key to not letting Excel use only the first 8 rows to guess the columns data type. Set this value to 0 to scan all rows. This might hurt performance. Please also note that adding the IMEX=1 option might cause the IMEX feature to set in after just 8 rows. Use IMEX=0 instead to be sure to force the registry TypeGuessRows=0 (scan all rows) to work.

If the Excel workbook is protected by a password, you cannot open it for data access, even by supplying the correct password with your connection string. If you try, you receive the following error message: "Could not decrypt file."

A workaround for the "could not decrypt file" problem

 
 

ACE OLEDB 12.0

TYPE OLE DB Provider
USAGE Provider=Microsoft.ACE.OLEDB.12.0
MANUFACTURER Microsoft

Excel 97-2003 Xls files with ACE OLEDB 12.0

You can use this connection string to use the Office 2007 OLEDB driver (ACE 12.0) to connect to older 97-2003 Excel workbooks.

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\myFolder\myOldExcelFile.xls;
Extended Properties="Excel 8.0;HDR=YES";

"HDR=Yes;" indicates that the first row contains columnnames, not data. "HDR=No;" indicates the opposite.

 
 

.NET Framework Data Provider for OLE DB

TYPE .NET Framework Wrapper Class Library
USAGE System.Data.OleDb.OleDbConnection
MANUFACTURER Microsoft

Use an OLE DB provider from .NET

Provider=any oledb provider's name;OledbKey1=someValue;OledbKey2=someValue;

See the respective OLEDB provider's connection strings options. The .net OleDbConnection will just pass on the connection string to the specified OLEDB provider. Read more here.

 
 

Microsoft Excel ODBC Driver

TYPE ODBC Driver
USAGE Driver={Microsoft Excel Driver (*.xls)}
MANUFACTURER Microsoft

Standard

Driver={Microsoft Excel Driver (*.xls)};DriverId=790;Dbq=C:\MyExcel.xls;
DefaultDir=c:\mypath;

SQL syntax "SELECT [Column Name One], [Column Name Two] FROM [Sheet One$]". I.e. excel worksheet name followed by a "$" and wrapped in "[" "]" brackets.

 
 

Specify ReadOnly

[Microsoft][ODBC Excel Driver] Operation must use an updateable query. Use this connection string to avoid the error.

Driver={Microsoft Excel Driver (*.xls)};Dbq=C:\MyExcel.xls;ReadOnly=0;

ReadOnly = 0 specifies the connection to be updateable.

 
 

Microsoft Excel 2007 ODBC Driver

TYPE ODBC Driver
USAGE Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)
MANUFACTURER Microsoft

Standard

Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=C:\MyExcel.xls;
 
 

.NET Framework Data Provider for ODBC

TYPE .NET Framework Wrapper Class Library
USAGE System.Data.Odbc.OdbcConnection
MANUFACTURER Microsoft

Use an ODBC driver from .NET

Driver={any odbc driver's name};OdbcKey1=someValue;OdbcKey2=someValue;

See the respective ODBC driver's connection strings options. The .net OdbcConnection will just pass on the connection string to the specified ODBC driver. Read more here.

 
 

.NET xlReader for Microsoft Excel

TYPE .NET Framework Class Library
USAGE VM.xPort.ExcelClient.ExcelConnection
MANUFACTURER xPortTools

Excel file with header row

Data Source =c:\myExcelFile.xls;HDR=yes;Format=xls;
 
 

Excel file without header row

Data Source =c:\myExcelFile.xls;HDR=no;Format=xls;
 
 


Posted by blueasa
TAG Excel, odbc, OLEDB

댓글을 달아 주세요



링크 : http://exceldatareader.codeplex.com/

Posted by blueasa

댓글을 달아 주세요

  • Excel 2007(xlsx) 파일 연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Extended Properties="Excel 12.0;HDR=YES";

 

  • Excel 2003 이전(xls) 파일 연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Extended Properties="Excel 8.0;HDR=YES";
  2.  Provider=Microsoft.JET.OLEDB.4.0;Data Source=FilePath;Extended Properties="Excel 8.0;HDR=YES";

 

  • Access 2007(accdb) 파일 연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;
  2. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Jet OLEDB:Database Password=Password; (패스워드가 존재할 경우)

 

  • Access 2003 이전(mdb) 파일  연결문자열
  1. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;
  2. Provider=Microsoft.ACE.OLEDB.12.0;Data Source=FilePath;Jet OLEDB:Database Password=Password; (패스워드가 존재할 경우)
  3. Provider=Microsoft.JET.OLEDB.4.0;Data Source=FilePath;

    Provider=Microsoft.JET.OLEDB.4.0;Data Source=FilePath;Jet OLEDB:Database Password=Password; (패스워드가 존재할 경우)

 

Microsoft.ACE.OLEDB.12.0 관련 오류 해결 방법

 
로컬에서 잘 되던 기능이 서버에서는 안되어서 인터넷을 검색해 보았습니다.

로컬에서는 오피스2007을 설치하면서 관련 컴포넌트가 같이 설치된 것 같습니다.

위와 같은 오류가 발생시 관련 컴포넌트를 설치해 주셔야 합니다.

* 다운로드 URL: http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=7554f536-8c28-4598-9b72-ef94e038c891




Posted by blueasa
TAG OLEDB

댓글을 달아 주세요

OleExcelReader

엑셀파일을 읽는 클래스이다. 
앞서 글에서도 언급을 했지만 OleDB를 이용한 방법은 다른 것보다 굉장한 속도를 자랑한다..^^;
단순히 읽거나 쓰거나 하는 거라면...이 방법을 사용하기 바란다.

다른 DB프로바이더와 마찬가지로 연결개체를 만들고(Connection) 만들어진 연결로 열고(Open) 데이터를 조작(Excute~)하고 마지막으로 닫는 (Close) 순서로 진행된다.
하나씩 살펴 보도록 하겠다.

 먼저 Connection을 살펴 보자

  261 private string ExcelConnection()

  262 {

  263   return

  264     @"Provider=Microsoft.Jet.OLEDB.4.0;" +

  265     @"Data Source=" + this.fileName + ";" +

  266     @"Extended Properties=" + Convert.ToChar(34).ToString() +

  267     @"Excel 8.0;" + ExcelConnectionOptions() + Convert.ToChar(34).ToString();

  268 }

기존의 sql 프로바이더와 비슷한 구조이다.
Provider가 Jet.OLEDB 라는거 빼면..비슷한 구조..^^;
Data Source는 출력할 파일의 이름을 선택하면 되겠다.

  247 private string ExcelConnectionOptions()

  248 {

  249   string strOpts = "Imex=" + this.imex + ";";

  250   if (this.hasHeaders)

  251   {

  252     strOpts += "HDR=Yes;";

  253   }

  254   else

  255   {

  256     strOpts += "HDR=No;";

  257   }

  258   return strOpts;

  259 }

추가로 헤더의 유무를 선택 할 수 있으며 IMEX 값을 선택 할 수 있다.
IMEX라는 속성은 중요하다..^^;
예전에 데브피아 팁게시판에 올려 놓은 링크를 보게 되면..^^;

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNo=8&no=986&ref=986

MSDN에 의하면 이 설명은 아래와 같다.

NOTE: Setting IMEX=1 tells the driver to use Import! mode. In this state, the registry setting Import!MixedTypes=Text will be noticed. This forces mixed data to be converted to text. For this to work reliably, you may also have to modify the registry setting, TypeGuessRows=8. The ISAM driver by default looks at the first eight rows and from that sampling determines the datatype. If this eight row sampling is all numeric, then setting IMEX=1 will not convert the default datatype to Text; it will remain numeric. 

You must be careful that IMEX=1 not be used indiscriminately. This is IMPORT! mode, so the results may be unpredictable if you try to do appends or updates of data in this mode. 

The possible settings of IMEX are:

<PRE class=FIXEDTEXT>0 is Export mode 1 is Import! mode 2 is Linked mode (full update capabilities)</PRE>

이 클래스는 Reader이므로 imex의 기본값은 1이 되겠다.
만약 Writer라면 ..당연히 0이 될것이다.

그 외의 속성은 첨부된 소스 코드를 참고 하도록 한다.

이제 생성된 ConnectionString으로 connection 개체를 만들고 Open 하도록 한다.

  289 this.con = new OleDbConnection(ExcelConnection());

  290 this.con.Open();

 이제 Select 할 Command 개체를 만들어 보겠다.

  313 private bool SetSheetQuerySelect()

  314 {

  315   try

  316   {

  317     if (this.con == null)

  318     {

  319       throw new Exception("Connection is unassigned or closed.");

  320     }

  321 

  322     if (this.sheetName.Length == 0)

  323     {

  324       throw new Exception("Sheetname was not assigned.");

  325     }

  326 

  327     string cmd = "";

  328     for (int i = 0; i < this.sheetName.Length; i++)

  329     {

  330       cmd += @"SELECT * FROM [" + this.sheetName[i] + "$" + this.SheetRange[i] + "]" + System.Environment.NewLine;

  331     }

  332     this.selectCmd = new OleDbCommand(cmd, con);

  333     return true;

  334   }

  335   catch (Exception ex)

  336   {

  337     throw ex;

  338   }

  339 }

330 라인에 주목하자.
SQL에서 사용하는 쿼리와 같은 형식의 SELECT 문이다.
해당 시트의 영역에서 컬럼들을 SELECT 한다는 의미가 되겠다.
이러한 쿼리로 332라인의 OleCommand 개체를 만들었다.
이제 쿼리를 실행만 하면 될것이다.

  382 //Fill table

  383 OleDbDataAdapter oleAdapter = new OleDbDataAdapter();

  384 this.selectCmd.CommandTimeout = 200;

  385 oleAdapter.SelectCommand = selectCmd;

  386 DataTable dt = new DataTable(strTableName);

  387 oleAdapter.FillSchema(dt, SchemaType.Source);

  388 oleAdapter.Fill(dt);

  389 if (this.hasHeaders == false)  //헤더가 없으면 임의로 컬럼명을 생성한다.

  390 {

  391   if (this.sheetRange[0].IndexOf(":") > 0)

  392   {

  393     string FirstCol = this.sheetRange[0].Substring(0, this.sheetRange[0].IndexOf(":") - 1);

  394     int intCol = this.ColNumber(FirstCol);

  395     for (int intI = 0; intI < dt.Columns.Count; intI++)

  396     {

  397       dt.Columns[intI].Caption = ColName(intCol + intI);

  398     }

  399   }

  400 }

  401 this.SetPrimaryKey(dt);

위 코드는 GetDataTable() 메서드의 일부이다.
OleDbDataAdpater 개체를 만들어서 388 라인처럼 Fill 하게 되면 dt 라는 데이터 테이블에 SELECT 한 데이터가 채워지게 된다.

구조는..간단하지 않은가????

이제 사용 사례를 살펴 보자..

    1 OleExcelReader reader = new OleExcelReader();

    2 reader.Filename = filePath;

    3 reader.HasHeaders = true;

    4 reader.KeepConnectionOpen = false;

    5 reader.PkColIndex = 0;

    6 string[] sheetNames = reader.GetExcelSheetNames();

    7 if (sheetNames.Length < 1)

    8 {

    9   exceptionMessage = "적어도 한개 이상의 Sheet가 있어야 합니다.";

   10   returnDataSet = null;

   11   return false;

   12 }

   13 else

   14 {

   15   reader.SheetName = new string[] { "List""Staphylococcus""E.coli""Salmonella""Shigella" };

   16   returnDataSet = reader.GetDataSet();

   17 

   18   if (returnDataSet.Tables.Count != 5)

   19   {

   20     exceptionMessage = "시트의 수가 맞지 않습니다.";

   21     return false;

   22   }

   23 }

6라인의 GetExcelSheetNames()라는 메서드로 해당 엑셀파일의 시트 이름의 배열을 얻을 수 있다.
이 배열로 해당 엑셀 파일이 몇개의 시트를 가지고 있는지 판단 할 수 있게 된다.

15라인을 보게 되면 SheetName 이라는 속성에 출력할 시트의 이름들의 배열을 할당 하는 것을 볼 수 있다.
그리고 나서 16라인의 GetDataSet() 메서드로 엑셀로 부터 쿼리해온 5개의 시트를 하나의 데이터셋으로 Fill 하는 작업을 할수 있다.

그외의 자잘한 작업이 있긴 하지만..
알고보면 아주..간단한 작업이 되겠다.

제공되는 샘플 소스는..꽤..지저분하다..
내가 처음부터 만든것도 아닐 뿐더러...불필요한 메서드 들도 꽤 있다...ㅡ.ㅡ;;;
혹시나 필요한 부분만 발췌해서 사용하고자 한다면.. 조금만 분석에 시간을 들여 보길..권한다.

다음은 OleExcelWriter에 대해서 간략히 알아 보자.

 

OleExcelWriter

ㅋ..내가 미쳤나 보다..ㅡ.ㅡ;
예제 코드를 만들고야 말았다..

이번 강좌는..이 예제 코드 중심으로 진행을 할 것이다..@.@

그러므로..예제 프로그램을 다운 받아 실행 해보면서 보는게...이해가 빠를 것이다...라고 생각된다.^^;

이 클래스의 사용 순서는 이렇다.

  1. 파일을 연다. (Open)
  2. 컬럼을 생성한다.
  3. 시트를 생성한다.
  4. 각 시트별 데이터를 인서트 한다. -> 인서트 할때 마다 이벤트를 발생시킨다.
  5. 파일을 닫는다.

앞서 살펴 보았던 Reader와 비슷하다.

   18     public delegate void RowCount(object sender, RowCountEventArgs e);

   19     public event RowCount OnRowCount;

하나의 row가 인서트 될때 마다 이벤트를 일으켜 클라이언트에게 알려 주기 위해 델리게이트와 이벤트를 만들었다.

RowCountEventArgs 클래스는 다음과 같이 정의 된다.

   11 public class RowCountEventArgs : System.EventArgs

   12   {

   13     private int rowCount = 0;

   14     private string tableName = "";

   15 

   16     ///

   17     /// Initializes a new instance of the class.

   18     ///

   19     /// The row count.

   20     public RowCountEventArgs(int rowCount)

   21     {

   22       this.rowCount = rowCount;

   23     }

   24 

   25     ///

   26     /// Initializes a new instance of the class.

   27     ///

   28     /// The row count.

   29     /// Name of the table.

   30     public RowCountEventArgs(int rowCount, string tableName)

   31     {

   32       this.rowCount = rowCount;

   33       this.tableName = tableName;

   34     }

   35 

   36     ///

   37     /// Gets the name of the table.

   38     ///

   39     /// The name of the table.

   40     public string TableName

   41     {

   42       get

   43       {

   44         return this.tableName;

   45       }

   46     }

   47 

   48     ///

   49     /// Gets the row count.

   50     ///

   51     /// The row count.

   52     public int RowCount

   53     {

   54       get

   55       {

   56         return this.rowCount;

   57       }

   58     }

   59   }

tableName과 rowCount 라는 필드를 만든 이유는 필요에 따라서 몇번째 테이블의 몇번째 row인지 보여줄 필요가 있겠다 싶어서 였다.
하나의 시트 라면 tableName은 필요 없는...게 되겠다. 생성자를 선택해서 사용하면 될것이다.

ConnectionString은 앞서 설명한 OleExcelReader와 같다. 단지 앞에서 언급한 대로 IMEX 속성의 값만 다를 뿐이다..기본값인 0을 유지 하기 바란다..

  145 public void Open()

  146 {

  147   try

  148   {

  149     if (con != null)

  150     {

  151       if (con.State == ConnectionState.Open)

  152       {

  153         con.Close();

  154       }

  155       con = null;

  156     }

  157 

  158     if (System.IO.File.Exists(this.fileName))

  159     {

  160       System.IO.File.Delete(this.fileName);

  161       //throw new Exception("파일이 이미 존재 합니다.");

  162     }

  163     con = new OleDbConnection(ExcelConnection());

  164     con.Open();

  165   }

  166   catch (Exception ex)

  167   {

  168     throw ex;

  169   }

  170 }

161라인의 파일의 존재 여부는 상황에 따라서..달라질듯 하지만..일단은 기존 파일은 삭제를 하는 방향으로 하였다.
본 예제처럼 SaveFileDialog 컨트롤을 사용하게 된다면..별 상관 없는 문제이지만 이 컨트롤을 사용하지 않거나 웹에서 사용하거나 하는 경우에는..문제가 생길 것이다..
웹에서 사용하는 경우에는 이 때문에..파일명을 시간으로 정해주는 것도 좋다..혹은 유일성을 보장하는 GUID 값이라든지...

두번째로는 컬럼을 생성하는 부분이다.

   41 string[] colType = new string[3];

   42 

   43 colType[0] = "int";

   44 colType[1] = "varchar(16)";

   45 colType[2] = "datetime";

Form1.cs 파일에서 보여지는 위 예처럼 코드로 직접 이렇게 컬럼의 형식을 입력해야 하는 이유는..C#의 형식과 SQL의 형식을 매칭 시킬수가 없기 때문이다.ㅡ.ㅠ;
int형과 datetime 형의 경우는 매칭을 시킬수가 있다고 친다지만..44라인의 varchar(16)은 c#의 string형을 변환 한 것이다..
이 때문에 만약 컬럼수가 많은 엑셀 파일의 경우에는...코드가 좀 많아진다는 단점이 있겠다..ㅡ.ㅠ;
string 형은 여러가지 sql 타입으로 변환 할 수 있다..varchar(사이즈), text 등...
그렇기에 이런식으로 string[] 로 타입을 순서대로..가진 List 형의 ColTypeList 라는 속성이 필요하다.
이 속성에 있는 string[] 은 하나의 시트에 들어 있는 컬럼의 배열을 뜻한다. 
그러므로 시트가 여러개 있는 엑셀파일이라면 그 시트의 수가 곧 ColTypeList.Count 가 되겠다.

세번째로 이제 시트를 생성할 차례이다.
시트는 SQL 쿼리문의 CREATE TABLE 로 생성한다.

  197 public void CreateSheet()

  198 {

  199   for (int i = 0; i < this.curDs.Tables.Count; i++)

  200   {

  201     this.Command("CREATE TABLE [" + this.curDs.Tables[i].TableName + "] (" + this.CreatColName(this.curDs.Tables[i], i) + ")");

  202   }

  203 }

위 구문은 CREATE TABLE ["시트명"] ( '컬럼명', '컬럼명',......) 이라는 쿼리문을 만들어 주어 쿼리(Command메서드)를 하는 메서드이다.
문맥으로 보아 CreateColName 이라는 메서드는 ( '컬럼명', '컬럼명',......) 라는 문자열을 만들어 주는 메서드라는 것을 짐작해 볼 수 있다.

네번째로 각 시트별 데이터를 인서트 할 차례이다.

  250 public void Binding()

  251 {

  252   foreach (System.Data.DataTable dt in this.curDs.Tables)

  253   {

  254     string col = "";

  255     string resultCol = "";

  256     foreach (DataColumn c in dt.Columns)

  257     {

  258       col += "[" + c.ColumnName + "],";

  259     }

  260     if (col.EndsWith(","))

  261     {

  262       resultCol = col.Substring(0, col.Length - 1);

  263     }

  264 

  265     for (int i = 0; i < dt.Rows.Count; i++)

  266     {

  267       this.Command(@"Insert Into [" + dt.TableName + "] (" + resultCol + ") values (" + this.CreateRowForInsert(dt, i) + ")");

  268       this.OnRowCount(thisnew RowCountEventArgs(++this.rowCount));

  269     }

  270   }

  271 }

252 라인의 foreach문을 보면 각각의 시트(DataTable)마다 인서트 하겠다는 것을 알 수 있다.
256~263라인은 인서트 할 컬럼을 만드는 것이다. 
267라인에 보면 INSERT INTO [테이블명] (컬럼명, 컬럼명, ....) VALUES (값, 값, ....) 형태의 인서트 구문을 만들어 쿼리 하는 것을 알 수 있다.
마지막으로 268 라인을 보면 이 강좌의 처음에 보여 주었던 이벤트를 발생 시키는 것을 볼 수 있다. 
이 이벤트의 매개변수로는 ++rowCount를 보낸다. 즉 하나의 라인을 인서트 한 후에 그 인서트한 행의 Count를 매개변수로 던지는 것이다.
그렇게 되면 클라이언트에서는 이 이벤트를 받아서 현재 몇번째의 데이터가 인서트 되어 있는지 알 수 있을 것이다.

자..이제 예제를 살펴 보도록 하겠다.(Form1.cs)
예제에는 실제로 progressbar와 label을 만들어서 현재 인서트 진행 상황을 보여주게끔 만들었다. 
아직 event와 delegate에 익숙하지 않은 분들도 약간의 참고가 될 수 있으리라 생각된다.

   16     private delegate void CurrentRowEventHandler(int rowIndex);

   17     private CurrentRowEventHandler rowCount;

   18     private DataSet ds = new DataSet();

16 라인을 보면 CurrentRowEventHandler 라는 델리게이트를 만들었고 17 라인에 이 델리게이트 형의 rowCount 라는 개체를 만들었다.
이 델리게이트는 OnRowCount 라는 이벤트를 받아서 그 때마다 UI 스레드에게 CallBack 하여 Label 컨트롤과 ProgressBar컨트롤을 작동시키기위해 필요한 존재이다.
그리고 18행은 예제에서 사용될 DataSet이다.

   65     private void Form1_Load(object sender, EventArgs e)

   66     {

   67       this.rowCount = this.CallBackRowCount;

   68       this.MakeDataSet();

   69     }

Form 이 Load가 되면 rowCount 라는 델리게이트는 CallBackRowCount 라는 델리게이트메서드에 연결을 시킨다. 
그리고 DataSet에 데이터를 채운다..
DataSet에는 2개의 테이블이 있으며 각각 int 형, string 형, datetime 형의 컬럼을 가지고 있으며 데이터는 500개씩 이다.
DataSet에 데이터를 채우는 내용은..예제를 참고 하도록 한다..

자 이제 버튼을 클릭하게 되면...스레드를 만들고 스레드를 시작한다.
이 스레드는 MakeExcelFile 이라는 메서드를 호출한다.

이제 이 MakeExcelFile 이라는 메서드를 살펴 보자.

   34 private void MakeExcelFile()

   35 {

   36   OleExcelWriter excel = new OleExcelWriter();

   37   excel.OnRowCount += new OleExcelWriter.RowCount(excel_OnRowCount);

   38 

   39   excel.FileName = this.saveFileDialog.FileName;

   40   excel.HasHeader = true;

   41   List<string[]> colList = new List<string[]>(2);

   42   string[] colType = new string[3];

   43 

   44   colType[0] = "int";

   45   colType[1] = "varchar(16)";

   46   colType[2] = "datetime";

   47 

   48   colList.Add(colType);

   49   colList.Add(colType);

   50 

   51   excel.ColTypeList = colList;

   52   excel.CurDs = this.ds;

   53   excel.KeepConnectionOpen = true;

   54   excel.CreateSheet();

   55   excel.Binding();

   56   excel.Dispose();

   57   excel.Close();

   58 }

36라인에서 OleExcelWriter의 개체를 생성하고 37라인에서 이벤트를 등록 하였다. 
40~51 라인은 컬럼을 생성하여 ColTypeList 속성에 대입시키는 코드이다. 앞에서 설명 했었다.
52 라인은 DataSet을 CurDs 라는 속성에 대입한다. 그리고 54 라인에서 시트를 생성하고 55라인에서 데이터를 인서트 하며, 56,57 라인에서 Close 하게 된다.

excel_OnRowCount 메서드를 살펴 보자면.

   60 void excel_OnRowCount(object sender, RowCountEventArgs e)

   61 {

   62   this.Invoke(this.rowCount, e.RowCount);

   63 }

단순히 Invoke 만 했다. 
Invoke 의 첫번째 매개변수는 rowCount 라는 델리게이트이며, 두번째 인자는 RowCountEventArgs.RowCount 이다. 
이 델리게이트를 호출(Invoke) 하게 되면 Form1_Load 이벤트 메서드에서 설정한 CallBackRowCount 메서드가 실행된다.

   71 private void CallBackRowCount(int rowIndex)

   72 {

   73   this.lblMessage.Text = String.Format("현재 {0}번째의 데이터를 쓰고 있습니다.", rowIndex.ToString());

   74   this.progressBar.PerformStep();

   75   Thread.Sleep(10);

   76 }

매개변수로 받은 rowIndex를 단지 label 컨트롤에 대입하고 progressbar를 performstep 하는 UI 컨트롤에 대한 설정 밖에 없다. 
제2 스레드에서 제1 스레드로 Invoke 하게 된 결과이다 
75라인은...너무 빨라서 진행상태를 보기에 어렵기에..살짝 텀을 준것 뿐이다...

이로서 간략하지만 OLEDB를 이용한 엑셀 핸들링을 마치도록 하겠다 
다음 시간(??)엔 jakarta.POI를 이용한 쓰기 방법을 알아 보자...^^;

 

 

Jakarta.POI 를 이용한 Excel Writing

자 이제 마지막 시간이다..^^;

http://jakarta.apache.org/poi/

위 URL을 참조 하면 jakarta.poi 에 대해서 정보를 얻을 수 있다.
이 어셈블리를 사용하기 위해서는 jakarta.poi.dll이 필요하며 웹의 경우 서버에 J#재배포패키지가 설치되어야 한다.
그리고 JSharplib.dll 파일을 참조 추가 하여야 한다.

자 이제 준비가 되었으니 사용법을 알아 보자.

먼저 Enum.cs 파일을 보게 되면 셀과 폰트에서 사용되는 열거형들이 정의되어 있다.
주석은 없지만...ㅡ.ㅡ;;;

외부에 노출되는 타입은 Enum.cs 에 정의되어 있는 열거형들과 ExcelHandler class, ExcelCellStyle class 이다.
ExcelCellSettingBase class는 internal로 정의되어 내부에서만 사용되는 클래스이다.
ExcelHandler 클래스는 엑셀파일을 생성하기위한 기본 클래스이다. 
ExcelCellStyle 클래스는 선택된 시트의 셀 영역에 적용할 스타일에 대한 클래스가 되겠다. 이르자면 폰트에 관련된 속성같은 것 말이다.

이 어셈블리의 구조를 먼저 설명을 해야 할 것 같다.

ExcelHandler 클래스는 내부적으로 List 형의 리스트를 가진다.  이 리스트는 DataSet형의 컬렉션인데 이 DataSet이 바로 하나의 Sheet이다.
즉 List.Count 는 시트의 갯수와 같다는 의미가 되겠다.
이제 이 DataSet의 구조를 살펴 보자.
이 DataSet에는 최소 1개 이상의 DataTable을 가진다.
이 DataTable은 하나의 데이터영역이다.
이르자면..타이틀영역, 빈공간, 데이터 넣는 곳 등등,,,
하나의 시트에 바인딩되는 데이터의 형태는 반드시 DataTable형태가 되어야 하며 이 DataTable들은 여러가지 형태로 시트에 표현이 될 수 있다.
샘플을 보게 되면 2개의 시트가 있는데 첫번째 시트의 경우에는 DataTable이 3개가 있다.  뒷 부분에서 설명하도록 하겠다.
그리고 시트의 설정에 대한 부분도 포함되어 있다. Split이라든지 Group 같은 기능들이 있다.

ExcelCellStyle 클래스는 데이터와 상관없이 셀의 스타일을 정의한다.
이 클래스는 테두리의 선 및 색상정의, 폰트의 종류,색상,크기등의 정의 등등의 속성이 있다.
이 클래스로 만들어진 스타일 인스턴스는 ExcelHandler.SetCellStyle()메서드로  시트에 임베디드된다.

이제 ExcelCellStyle 클래스부터 살펴 보겠다.

위 클래스 다이어그램을 보는 바와 같이 ExcelCellStyle 클래스는 모두 필드와 속성으로만 정의되어 있으며 내부 클래스인 Font 클래스를 가지고 있다.
이 클래스는 FontStyle이라는 속성으로 사용된다.
Align, Rotation, CellColor, Border, Font를 설정할 수 있다.

이제 핵심 클래스인 ExcelHandler 클래스를 살펴보자.

   11 using org.apache.poi.hssf.util;

   12 using org.apache.poi.hssf.usermodel;

이 어셈블리를 사용하기 위해서는 위처럼 2개의 네임스페이스를 등록하여야 한다.

   48     public delegate void RowCount(object sender, RowCountEventArgs e);

   49     public event RowCount OnRowCount;

앞서 OleExcelWriter에서 설명한 바와 같이 OnRowCount 라는 이벤트와 델리게이트를 생성하였다.
앞서 설명을 하였기 때문에 패스..

public ExcelHandler();

public ExcelHandler(List<string> sheetName);

public ExcelHandler(string fullName);

public ExcelHandler(string fullName, List<string> sheetName);

public ExcelHandler(string path, string fileName);

public ExcelHandler(string path, string fileName, List<string> sheetName);

생성자는 위와 같이 정의 되어 있다.
2개 이상의 시트가 있거나 시트의 이름을 정해주고자 할 때는 sheetName 매개변수를 이용하도록 한다. 주의사항은 한글이름은 지원이되지 않는다..ㅡ.ㅠ;

  789 ///

  790 /// 개체 생성

  791 ///

  792 private void Create()

  793 {

  794   this.wb = new HSSFWorkbook();

  795   foreach (string sheet in this.sheetName)

  796   {

  797     this.wb.createSheet(sheet);

  798   }

  799 }

생성자에서는 위의 Create()메서드를 호출하며 이 메서드에서는 마치 Automation과 마찬가지로 워크북을 생성한다음에  sheetName 리스트에서 할당된 갯수만큼 시트를 만든다.

이제 시트를 만들었다면 Data를 입력해야할 차례이다..다음 메서드를 살펴보자.

  506 public void InsertTable(int sheetIndex, bool hasTitle, DataTable dt)

  507 {

  508   if (this.dsList.Count == sheetIndex)

  509   {

  510     DataSet ds = new DataSet();

  511     this.dsList.Add(ds);

  512   }

  513   this.dsList[sheetIndex].Tables.Add(dt);

  514 

  515   if (this.hasTitleList.Count == sheetIndex)

  516   {

  517     StringCollection sc = new StringCollection();

  518     this.hasTitleList.Add(sc);

  519   }

  520   this.hasTitleList[sheetIndex].Add(hasTitle.ToString());

  521 }

매개변수를 보자면.

  1. sheetIndex - 데이터를 넣을 시트의 인덱스
  2. hasTitle - 데이터테이블의 경우 해당 컬럼의 이름을 데이터의 상단에 렌더 할수 있다. 이 제목의 사용 여부이다.
  3. dt - 시트에 입력될 데이터테이블

처음에 구조를 설명하였듯이...
dsList 라는 제너릭리스트는 하나의 워크북을 의미하고 그 안에 있는 ds 라는 데이터셋은 하나의 시트를 의미하며 그 안에 입력되는 dt는 하나의 데이터영역이다.
hasTitleList는 hasTitle의 bool 값을 저장하고 있는 문자열컬렉션(StringCollection)의 제너릭리스트이다. 
dsList와 hasTitleList의 인덱스는 정확히..매칭된다.

  530 public void InsertBlankRow(int sheetIndex, int rowCount)

  531 {

  532   DataTable dt = new DataTable();

  533   dt.Columns.Add(new DataColumn());

  534   for (int i = 0; i < rowCount; i++)

  535   {

  536     DataRow dr = dt.NewRow();

  537     dr[0] = "";

  538     dt.Rows.Add(dr);

  539   }

  540   this.InsertTable(sheetIndex, false, dt);

  541 }

InsertBlackRow() 메서드는 해당 시트에 빈 공간(row)을 삽입하는 메서드이다.
몇 row의 빈칸을 삽입할지 결정하여 호출 하면 540라인에서 보듯이 InsertTable메서드를 호출하며 해당 DataSet(Sheet)에 값이 없는 (스키마만 있는) DataTable을 삽입하게 된다.

이제 데이터를 바인딩 하기 전에 바딩딩 할때 적용되로 스타일을 세팅 하도록 한다.

public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, CellType type);

public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, ExcelCellStyle cellStyle);

public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, ExcelCellStyle cellStyle, CellType type);

스타일을 세팅하기위한 메서드는 SetCellStyle 메서드인데 3가지로 overload되어 있다.
매개변수를 살펴 보자.

  • sheetIndex - 스타일을 적용할 시트의 인덱스
  • startColumnIndex - 스타일을 적용할 영역의 시작 컬럼 인덱스
  • endColumnIndex - 스타일을 적용할 영역의 끝 컬럼 인덱스
  • startRowIndex - 스타일을 적용할 영역의 시작 로우 인덱스
  • endRowIndex - 스타일을 적용할 영역의 끝 로우 인덱스
  • cellStyle - ExcelCellStyle의 개체참조
  • type - 셀의 타입, 현재 정의된 타입은 string, int, datetime 형 3가지이다.

  301 public void SetCellStyle(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex, ExcelCellStyle cellStyle, CellType type)

  302 {

  303   if (this.wb == null)

  304   {

  305     throw new Exception("워크북이 생성되지 않았습니다.");

  306   }

  307   else

  308   {

  309     if (this.wb.getSheetAt(sheetIndex) == null)

  310     {

  311       throw new Exception(sheetIndex + "인덱스에 시트가 생성되지 않았습니다.");

  312     }

  313     else

  314     {

  315       HSSFCellStyle style = this.wb.createCellStyle();

  316       this.AttachStyle(ref style, cellStyle, type);

  317       this.settingList.Add(new ExcelCellSettingBase(sheetIndex, startColumnIndex, endColumnIndex, startRowIndex, endRowIndex, style));

  318     }

  319   }

  320 }

위 메서드를 살펴 보자.
먼저 HSSFCellStyle 형의 셀의 스타일 개체를 생성한다.
그리고 ExcelCellStyle 개채의 값을 style 개체에 attatch를 하고 ExcelCellSettingBase 형의 개체를 생성하고 settingList 라는 제너릭리스트에 Add 한다.
ExcelCellSettingBase 클래스는 스타일이 정의된 영역에 대한 정보를 가지고 있다. 그러므로 settingList 라는 제너릭 리스트는 이 스타일이 정의된 영역에 대한 정보를 가진 컬렉션이다.

이제 바인딩을 해보자.

  548 public void DataBinding()

  549 {

  550   if (this.dsList.Count == 0)

  551   {

  552     throw new Exception("데이터가 없습니다.");

  553   }

  554   else

  555   {

  556     //하나의 DataSet이 하나의 시트

  557     for (int j = 0; j < dsList.Count; j++)

  558     {

  559       this.sheetIndex = j;

  560       int totalRowCountInSheet = this.SumOfTableRowsCount(j);

  561 

  562       int rowCount = 0;

  563 

  564       //데이터 갯수와 헤더의 갯수(있을거라고 가정)가 65536을 넘을 수 없다.

  565       //헤더는 각각의 테이블당 한개의 row를 차지하므로 한개의 시트에 속하는 테이블의 갯수와 동일하다.

  566       //하나의 시트의 Row가 65535를 넘게 되면 오류...

  567       totalRowCountInSheet += dsList[j].Tables.Count;

  568 

  569       if (totalRowCountInSheet > 65535)

  570       {

  571         throw new ArgumentOutOfRangeException("데이터의 수가 65535건이 넘었습니다.");

  572       }

  573       else

  574       {

  575         for (int i = 0; i < dsList[j].Tables.Count; i++)

  576         {

  577           if (Convert.ToBoolean(this.hasTitleList[j][i]))

  578           {

  579             this.CreateTitle(j, dsList[j].Tables[i].Columns, rowCount);

  580             rowCount++;

  581           }

  582           this.DataBinding(dsList[j].Tables[i], rowCount);

  583           rowCount += dsList[j].Tables[i].Rows.Count;

  584         }

  585       }

  586     }

  587   }

  588 }

지금까지 정의 된 값을 토대로 하여 데이터를 바인딩 한다.
560라인의 SumOfTableRowCount()메서드는 해당 시트에 바인딩 될 총 row의 합계이다.
이를 조사하는 이유는..
엑셀의 경우 하나의 시트에 65536행까지만 허용되기 때문이다.
그러나 Excel 2007의 경우에는 1백만행*65536컬럼 까지 지원한다고 한다..
579라인에 보면 CreateTitle()메서드가 보이는데 제목을 가진 DataTable이라면(577라인의 조건절) 제목을 시트에 쓴다.
그리고 582라인의 DataBinding 메서드에서 실제로 시트에 쓴다.

먼저 CreateTitle()메서드를 살펴보자.

  745 HSSFSheet sheet = this.wb.getSheetAt(sheetIndex);

  746 HSSFRow row = sheet.createRow(rowCount);

  747 for (short i = 0; i < dcc.Count; i++)

  748 {

  749   if (!dcc[i].ColumnName.StartsWith("Column"))

  750   {

  751     HSSFCell cell = row.createCell(i);

  752     cell.setEncoding(1);

  753     this.currentRowIndex = rowCount;

  754     this.currentColumnIndex = i;

  755     ExcelCellSettingBase cellStyle = this.settingList.Find(FindStyle);

  756     if (cellStyle != null)

  757     {

  758       HSSFCellStyle cs = cellStyle.CellStyle;

  759       cell.setCellStyle(cs);

  760     }

  761     cell.setCellValue(dcc[i].ColumnName);

  762   }

  763 }

발췌했다.
745라인에서 sheet개체를 얻고 746라인에서 createRow()메서드로 row를 만들었다.
749라인의 조건절은 자동생성된 컬럼을 걸르기 위함이다. 자동으로 생성된 컬럼은 의미가 없기 때문이다 . 자동생성된 컬럼의 이름패턴은 Column1, Column2..... 처럼 생성된다.
755라인에 주목하자. 
제너릭리스트의 Find()메서드를 사용하였다. 닷넷 2.0에서 새로나온 개념이다..^^; 
이 Find 메서드는 settingList 라는 제너릭 리스트에서 해당 조건에 맞는 첫번째 아이템을 반환한다.

  806 private bool FindStyle(ExcelCellSettingBase setting)

  807 {

  808   if (setting.SheetIndex == this.sheetIndex && setting.StartRowIndex <= this.currentRowIndex && setting.EndRowIndex >= this.currentRowIndex && setting.StartColumnIndex <= this.currentColumnIndex && setting.EndColumnIndex >= this.currentColumnIndex)

  809   {

  810     return true;

  811   }

  812   else

  813   {

  814     return false;

  815   }

  816 }

FindStyle 메서드를 살펴 보면 앞서 정의했던 settingList 제너릭 리스트에서 현재 Cell의 영역이 포함되어 있는 첫번째 ExcelCellSettingBase 개체를 반환한다.
음..한번도 안써보신 분이라면..약간 헤깔릴 수도....

어쨋든 해당되는 부분을 찾았으면 759라인처럼 셀에 스타일을 정의한 후에 761라인 처럼 값을 입력한다.

이제 582라인의 DataBinding 메서드를 살펴 보자.

  620 HSSFCellStyle cs = cellStyle.CellStyle;

  621 c.setCellStyle(cs);

  622 object o = dt.Rows[rowIndexInCurrentTable][currentColumnIndex];

  623 if (o.ToString() != "")

  624 {

  625   switch ((CellType)cs.getDataFormat())

  626   {

  627     case CellType.CELL_TYPE_NUMERIC:

  628       c.setCellValue(Convert.ToDouble(o));

  629       break;

  630     case CellType.CELL_TYPE_DATETIME:

  631       c.setCellValue(new java.util.Date(Convert.ToDateTime(o).ToLongTimeString().Trim()));

  632       break;

  633     case CellType.CELL_TYPE_STRING:

  634       c.setCellValue(o.ToString().Trim());

  635       break;

  636   }

  637 }

  638 else

  639 {

  640   c.setCellValue("");

  641 }

역시 발췌했다.
CreateTitle() 메서드와 마찬가지로 Find 메서드로 해당되는 ExcelCellSettingBase 개체를 반환하여 적용한다.
그리고 추가로 작업을 하나 더 하는데..
바로 셀의 타입을 결정하는 부분이다.
셀의 타입 역시 HSSFCellStyle 개체에 포함되어 있는 부분이다.
그렇기에 셀의 형식에 맞게끔 데이터를 형변환해서 입력해주어야 한다.
그 부분이 switch ~case 구문이다.
631라인의 경우에는 datetime형식을 표현하는 방식이 닷넷과 자바가 달라서..저런 방법을 취할수 밖에 없었다...쿨럭. 그리고 마지막으로 이벤트를 발생 시킨다.

  655 this.OnRowCount(thisnew RowCountEventArgs(rowIndexInCurrentTable + 1, dt.TableName));

지금까지 작업을 하였다면 하나의 워크북에 시트와 데이터가 모두 만들어 졌을 것이다.
이제 약간의 추가 작업을 선택적으로 해주면 되겠다.

추가적인 기능은

public void ColumnWidth(int sheetIndex, short columnIndex, short width);

public void GroupColumn(int sheetIndex, short startColumn, short endColumn);

public void GroupRow(int sheetIndex, int startRow, int endRow);

public void Merge(int sheetIndex, short startColumnIndex, short endColumnIndex, int startRowIndex, int endRowIndex);

public void RowHeight(int sheetIndex, int startRowIndex, int endRowIndex, int height);

public void Split(int sheetIndex, int colSplit, int rowSplit);

위와 같다.

  • ColumnWidth - 컬럼의 폭을 결정한다, 모든 단위는 px 단위이다.
  • GroupColumn - 컬럼을 그룹화한다.
  • GroupRow - 로우를 그룹화한다.
  • Merge - 정의된 셀영역을 합친다.
  • RowHeight - 로우의 높이를 결정한다.
  • Split - 문서를 스플릿한다.

주의 할 것은 이 메서드들은 반드시 바인딩 후에 호출하여야 한다.는 것이다.

이제 마지막 단계이다.
이렇게 워크북을 만들었다면..이제 출력을 해야 할것이다.
출력은 2가지가 지원한다.
하나는 파일 출력이며 하나는 스트림출력이다.

  664 public void OutPut()

  665 {

  666   string fPath = this.fullName;

  667   if (fPath == "")

  668   {

  669     fPath = this.path + this.fileName;

  670   }

  671   using (Stream fileOut = new FileStream(fPath, FileMode.Create))

  672   {

  673     this.wb.write(fileOut);

  674   }

  675 }

OutPut()메서드는 파일 출력이다. 673라인의 write() 메서드로 정의된 파일명으로 파일이 생성된다.
윈도우즈 응용프로그램에서 사용하기 적합하다.

  681 public byte[] OutPutToWeb()

  682 {

  683   Stream fileOut = new MemoryStream();

  684   this.wb.write(fileOut);

  685   byte[] buffer = ((MemoryStream)fileOut).GetBuffer();

  686   fileOut.Close();

  687   return buffer;

  688 }

이 메서드는 byte[] 배열형으로 출력한다.  
웹의 경우 서버에 파일을 만들필요가 없기에 스트림으로 내려 주는 방식이 좋다. 샘플 예제는 웹방식으로 적용해 보았다.

지금까지...어셈블리에 대해서 알아 보았다.
이제 샘플코드를 보고..어떻게 사용하는지 알아보자.

   23 List<string> sheetNames = new List<string>(2);

   24 sheetNames.Add("sheet1");

   25 sheetNames.Add("sheet2");

   26 

   27 ExcelHandler handler = new ExcelHandler(sheetNames);

   28 handler.OnRowCount += new ExcelHandler.RowCount(handler_OnRowCount);

   29 

   30 handler.InsertTable(0, falsethis.MakeTitle());

   31 handler.InsertBlankRow(0, 3);

   32 handler.InsertTable(0, truethis.MakeDataTable("테스트"));

   33 handler.InsertTable(1, falsethis.MakeTitle());

   34 handler.InsertTable(1, truethis.MakeDataTable("테스트"));

   35 

   36 handler.SetCellStyle(0, 0, 0, 0, 0, this.MakeCellStyle1());

   37 handler.SetCellStyle(0, 0, 0, 10, 50, this.MakeCellStyle2(), CellType.CELL_TYPE_NUMERIC);

   38 handler.SetCellStyle(0, 1, 1, 10, 50, this.MakeCellStyle2(), CellType.CELL_TYPE_STRING);

   39 handler.SetCellStyle(0, 2, 2, 10, 50, this.MakeCellStyle2(), CellType.CELL_TYPE_DATETIME);

   40 

   41 handler.DataBinding();

   42 

   43 handler.RowHeight(0, 0, 0, 100);

   44 handler.ColumnWidth(0, 0, 300);

   45 handler.Merge(0, 0, 2, 0, 0);

   46 handler.Split(0, 0, 5);

   47 handler.GroupRow(0, 5, 10);

   48 

   49 //파일 다운로드

   50 this.Response.Clear();

   51 this.Response.AddHeader("Content-Disposition""attachment; filename=exceltest.xls");

   52 this.Response.ContentType = "application/vnd.ms-excel ";

   53 this.Response.BinaryWrite(handler.OutPutToWeb());

   54 this.Response.End();

22~25 라인에서 시트의 이름을 생성했다. 다시 말하지만 한글은 지원하지 않는다.
27라인에서 생성자를 만들고
28라인에서 이벤트를 등록하였다.(웹에서는 사실 이 이벤트가 소용없으나 선언된것이기 때문에 정의하지 않으면 예외를 던진다.)

30~34라인에서 DataTable을 각각의 시트에 입력하였다. 첫번째 시트에는 공란도 입력하였다. 

36~39 라인에서 스타일과 타입을 정의 하였다. 영역이 중복되어도 상관없지만..먼저 정의된것만 적용된다.^^;  테스트를 위하여 각각 숫자형, 문자형, 일시형을 정의해 보았다.

그리고 41라인 처럼 바인딩을 한다.

43~47라인에는 시트의 설정을 변경해 보았다. 어떻게 출력이 될까 하고 예측해보자...

50~54 라인에서는 웹상에서 출력하는 방법을 보여준 예이다.
주목 할 것은 바로 53라인이다.
Response.BinaryWriter() 메서드로 출력하는 것을 확인하기 바란다.

이제 샘플코드를 실행해서 엑셀파일을 다운 받아 보면..아래와 같은 결과가 나오겠다.

그리고 ..

마지막으로 스타일을 정의 하는 부분은 같이 업로드된 파일 처럼 코드스니핏을 이용하면 편하다.
혹시 이용법을 모르는 사람을 위해..
첨부된 excelStyle.snippet 파일을 C:\Documents and Settings\사용자이름\My Documents\Visual Studio 2005\Code Snippets\Visual C#\My Code Snippets 경로에 붙여 넣기를 한다.
그리고 VS2005를 재시동한다.

이제 화면에서 excel이라고 치면 인텔리센스가 뜬다...그게 코드 스니핏이다.



[출처] C#에서 OLEDB를 이용한 엑셀(EXCEL)파일 읽기 및 쓰기|작성자 이카루스


Posted by blueasa
TAG Excel, OLEDB

댓글을 달아 주세요

var 키워드

Programming/C# / 2012. 12. 9. 22:08

C# 3.0의 새로운 기능(part1: var키워드를 사용한 타입유추)

C#3.0이 등장 하면서 여러 가지 새로운 기능이 추가 되었습니다. 타입유추, 익명타입, 람다 표현식, 개체/컬렉션 초기화, Nullable 타입, 확장 메서드, 쿼리 키워드, 자동으로 구현된 속성(Automatic Property, 부분 매서드(partial method) 등등 상당히 많은 새로운 기능들이 추가 되었습니다. 이 기능 중 대부분의 기능은 LINQ의 기반이 되며 LINQ를 배우기 이전에 먼저 학습할 필요가 있습니다.

이번 강좌를 시작으로 앞에서 언급한 새로운 기능 중 LINQ의 기반이 되는 새로운 기능들에 대해 강좌를 연재 하려고 합니다. 언제 강좌가 마무리 될지 확실하지는 않지만 최대한 주기적으로 강좌를 연재하고 최대한 빠른 시간 안에 마무리 하도록 노력하겠습니다.

C# 3.0부터 지역 변수의 타입을 반드시 명시적으로 지정해줄 필요 없이 var키워드를 사용하여 컴파일러가 타입을 유추할 수 있도록 할 수 있습니다. 이는 뒤에서 배우게 될 명시적으로 타입을 지정하지 않는 익명 타입이나 가독성이 떨어지는 중첩된 제네릭(예: IEnumerable<IEnumerable<Product>>) 과 같이 특수한 경우에 var키워드를 사용하여 좀더 간단하게 타입을 표현할 수 있습니다. 
다른 개발자가 코드를 보았을 때 이해 할 수 있는 수준으로 작성이 되면 var키워드를 통한 타입 유추는 좀더 간결한 코드를 만들어 낼 수 있는 훌륭한 기능이라 할 수 있겠습니다.

1. 타입유추는 어떻게 이루어 지는가?

var키워드는 변수를 선언하고 그 값을 할당하는 초기화 구문에서 '='을 기준으로 오른쪽 부분의 형태에 따라 컴파일러에게 타입유추를 지시 하게 됩니다.

var i = 5

위의 코드를 보면 5라는 값이 변수 i에 할당되므로 컴파일러는 5라는 값이 숫자 즉 int타입이라는 것을 미리 알고 있기 때문에 변수 i를 int타입으로 유추하게 되는 것 입니다.

//string타입으로 유추 
var s = "Taeyo.NET"; 
//double타입으로 유추 
var d = 12.5; 
//char타입으로 유추 
var c = 'k';

C#의 기본 타입 뿐만 아니라 사용자 정의 타입(Class) 및 익명 타입(Anonymouse Type) 또한 유추 될 수 있습니다.

그럼 간단하게 사용자 정의 타입(Class)을 유추하는 코드를 살펴 보도록 하겠습니다.

Class Person 

    public string FirstName; 
    public string LastName; 
    public int Age; 


Person person = new Person(); 
person.FirstName = "경균"; 
person.LastName = "김"; 
person.Age = 19; 

//사용자 지정 타입으로 유추 
var p = person;

Person이라는 새로운 Class를 만들고 개체를 생성한 후 var 키워드로 선언된 p라는 변수에 person개체를 할당하게 되면 컴파일러에 의해 변수 p는 Person타입으로 유추 됩니다.

익명 타입 또한 컴파일러에 의해 유추가 가능 합니다. 
아래 코드를 보면 식의 오른쪽 부분이 익명 타입이고 그 타입을 변수 p가 받게 되면 컴파일러에 의해 변수 p는 익명 타입으로 유추가 됩니다.

//익명타입으로 유추 
var p = new { FirstName = "경균", LastName = "김", Age = 19 };

위의 예제 코드에서는 익명타입의 형태만 알아 두고 자세한 내용은 다음 강좌에서 자세히 알아 보도록 하겠습니다.

다음은 쿼리표현식의 타입을 유추하는 코드 입니다.

// IEnumerable타입으로 유추 
var products = 
      from p in Products 
      where p.ProductName == "chai" 
      select p;

위의 쿼리 표현식은 LINQ를 활용함에 있어서 가장 많이 사용되는 구조입니다. 코드에 대해 간단히 알아보면 Products테이블에서 ProductName이 ‘Chai’와 같은 상품을 출력하는 구문으로 그 결과는 IEnumerable<T>타입입니다. 그렇기 때문에 products변수는 IEnumerable<T>타입으로 유추 됩니다. 쿼리 표현식은 마지막 강좌에서 자세히 다루도록 할 것입니다.

2. 정말로 유추를 하는가?

간단한 예제 코드를 컴파일 하고 실제 어떻게 타입유추가 일어나는지를 확인 하기 위해 IL DisAssembler(ildasm.exe)를 사용하여 컴파일된 IL코드를 확인 해 보도록 하겠습니다.

아래는 Int타입과 직접 작성한 Person타입을 이용하는 예제 코드 입니다.

Class Person 

    public string FirstName; 
    public string LastName; 
    public int Age; 


class TypeInference 

    public int GetInt() 
    { 
        var i = 10; 
        int b = 11; 
        var p = new Person(); 
        return i; 
    } 
}

타입유추를 테스트 하기 위해 변수 i는 var키워드를 통해 초기화 했고 변수 b는 일반적인 방식으로 int타입으로 선언 하였습니다. 그리고 사용자 정의 타입인 Person타입을 변수 p에 할당 했습니다. 
위의 코드를 컴파일 한 후 IL DisAssembler를 사용해 IL코드를 확인 해 보면 아래와 같은 코드를 볼 수 있습니다.

변수 i와 변수 b 모두 int32형태로 초기화 된 것을 확인 할 수 있으며 변수 p는 정확히 Person타입으로 유추된 것을 확인 할 수 있습니다.

3. var키워드의 사용 범위

var키워드는 아무데서나 사용할 수 있는 것은 아닙니다. 매서드 내부의 지역변수나 for문, foreach문, using문의 초기화 구문에서 사용될 수 있습니다.

매서드 내의 지역변수로서 사용

public int GetTotalCount() 
{
    var cnt = db.TotalCount(); 
    return cnt; 
}

for문의 초기화 구문에서 사용

for(var i = 0 ; i <10 ; i++){…}

foreach문의 초기화 구문에서 사용

foreach(var dr in dt.Rows){…}

using문의 초기화 구문에서 사용

using(var file = new StreamReader("c:\test.txt")){….}

4. var키워드를 사용하면서 이것만은 주의하자.

var 키워드는 자바스크립트나 VB의 "Variant"로 오해할 수 있습니다. 하지만 C#의 var키워드는 런타임에 바인딩 되지 않고 컴파일 타임에 컴파일러가 적합한 타입을 결정하고 할당하게 됩니다. 
그렇기 때문에 컴파일러가 적합한 타입을 결정하려면 선언과 동시에 초기화가 이루어 져야 하며 Null은 사용할 수 없습니다.(Null은 타입이 없기 때문이죠) 또한 클래스 범위에서는 사용할 수 없고 반드시 매서드 내의 지역변수로 사용해야 합니다.

var키워드의 상당히 편리하지만 명시적으로 타입이 선언되어 있지 않기 때문에 다른 개발자가 코드를 보게 되면 코드를 이해하기 난해 할 수 있습니다. var키워드를 반드시 필요한 경우(익명타입, 쿼리표현식의 결과타입, 중첩된 제네릭 타입 등)에 사용하면 보다 나은 개발 편의성 및 생산성 향상에 도움이 될 것입니다. 타입유추는 LINQ를 사용하기 위해 반드시 알아야 할 중요한 내용이기 때문에 첫번째 강좌에서 이렇게 다루었습니다. 다음 강좌는 익명타입 과 개체/컬렉션 초기화에 대해 알아 보도록 하겠습니다. 
감사합니다.


출처 : http://www.taeyo.pe.kr/Lecture/ASPNET2/KK_CSharp3_01.asp


Posted by blueasa
TAG VAR

댓글을 달아 주세요