////// Excel /// .Application 액셀 어플리케이션 자체 /// ._Workbook 워크북 = 엑셀 파일 /// .Sheets 엑셀 시트의 배열 /// ._Worksheet 액티브된 단일 시트 /// .Range 편집범위 /// using System; using System.Text; using Excel=Microsoft.Office.Interop.Excel; using System.Windows.Forms; using System.Diagnostics; namespace automating_system_report { class ExcelUpdater:FileManager { #region const var #endregion const var #region public global var public Excel.Application objApp = new Excel.Application(); public Excel._Workbook objBook; #endregion public global var #region 생성자&소멸자 public ExcelUpdater() //기본 생성자 { } public ExcelUpdater(string sourcefile, string targetfile)//템플릿 파일과 생성할 파일을 파라미터로 주고 open { LoadFile(sourcefile,targetfile); } ~ExcelUpdater() { //PRB: Visual Studio .NET 클라이언트에서 자동화 후에 Office 응용 프로그램이 종료되지 않는다(http://support.microsoft.com/kb/317109) UnLoadFile(); } #endregion 생성자&소멸자 #region public Method ////// updateSheet, 배열의 내용으로 sheet를 채운다. /// 줄과 칸을 각각 받았으나 "A1" 형식의 위치를 받기로 변경함. /// /// 데이터를 붙여넣을 sheet번호. 1부터 시작 /// 붙여넣기 시작할 위치, (예: "A1") /// 붙여넣을 내용, 문자열 2차원 배열 public bool updateSheet(int sheetNumber, string writeStartLoc, ref string[,] rawdata) { try { Excel.Sheets objSheets = objBook.Sheets; Excel._Worksheet objSheet = objSheets[sheetNumber]; Excel.Range range; range = objSheet.get_Range(writeStartLoc); range = range.get_Resize(rawdata.GetLength(0), rawdata.GetLength(1)); range.set_Value(null, rawdata); return true; } catch(Exception e) { MessageBox.Show(e.Message); return false; } } /// col과 row로 호출하면, "A1"형식으로 변환. /// 데이터를 붙여넣을 sheet번호. 1부터 시작 /// 붙여넣기 시작할 줄, 1부터 시작 /// 붙여넣기 시작할 칸, 0부터 시작(0~16383) /// 붙여넣을 내용, 문자열 2차원 배열 public void updateSheet(int sheetNumber,int writeStartRow,int writeStartCol,ref string[,] rawdata) { // Row와 Col을 저장할 변수들 string writeStartLoc; writeStartLoc = DecToAlphabet(writeStartCol) + writeStartRow.ToString(); // row 1, col 0일 경우 loc은 "A1" updateSheet(sheetNumber, writeStartLoc, ref rawdata); } public bool LoadFile(string TemplateXlsFile, string CopiedXlsFile) // source파일의 사본을 만들고 로드 { FullPath = CopiedXlsFile; try // 엑셀파일이 열려있을 경우 오류 { //System.IO.File.Delete(FullPath); System.IO.File.Copy(TemplateXlsFile, CopiedXlsFile, true); //템플릿을 복사 objBook = objApp.Workbooks.Open(CopiedXlsFile); // objApp.Visible = true; // Excel app의 visible=false; return true; } catch(Exception e) { MessageBox.Show(e.Message); return false; } } public void UnLoadFile() // 엑셀 프로세스를 저장없이 그냥 닫을 때 사용. { objBook = null; objApp = null; } public bool CloseAndSave() // 엑셀 파일 저장. { try { objBook.Save(); objBook.Close(); objApp.Quit(); //프로세스에서 삭제 return true; } catch (Exception e) { MessageBox.Show(e.Message); return false; } } ////// 10진수 컬럼 값을 엑셀에서 사용하는 컬럼 값 처럼, 알파벳으로 변환 /// /// 0은 A, Z는 25 ///public string DecToAlphabet(int num) { int rest; //나눗셈 계산에 사용될 나머지 값 string alphabet; //10진수에서 알파벳으로 변환될 값 byte[] asciiA = Encoding.ASCII.GetBytes("A"); // 0=>A rest = num % 26; // A~Z 26자 asciiA[0] += (byte)rest; // num 0일 때 A, num 4일 때 A+4 => E alphabet = Encoding.ASCII.GetString(asciiA); //변환된 알파벳 저장 num = num / 26 -1; // 그 다음 자리의 알파벳 계산을 재귀하기 위해, 받은 수/알파벳수 -1 (0은 A라는 문자값이 있으므로 -1을 기준으로 계산함) if (num > -1) { alphabet = alphabet.Insert(0, DecToAlphabet(num)); //재귀 호출하며 결과를 앞자리에 insert } return alphabet; // 최종값 return } /// int input=Int32.Parse(textBox1.Text.ToString()); /// Trace.WriteLine(input+"=> "+ xlsmaker.DecToAlphabet(input)); /// 결과 /// 0=> A /// 27=> AB /// 16383=> XFD #endregion public Method } } //
2012-06-25
C# 배열의 내용을 Excel sheet에 채우기
2012-06-22
C# Excel worksheet.Cells의 성능 느림.
출처: http://support.microsoft.com/kb/306023/ko
전에 올린 Cell에 직접 데이터를 입력하는 방법은 쉬운 방법이지만,
대량의 데이터를 옴기기엔 속도가 매우 느리다.(문서에서는 300줄을 기준으로 삼음)
8600줄의 데이터를 엑셀파일로 옴겼더니, 20분째 어플리케이션은 행이 걸려있고 엑셀문서는 한줄 한줄 타이핑 잘하는 사람이 입력하는 걸 빨리감기 하는 정도 속도로 쓰고 있다.
Cell에 직접 데이터를 입력하는 방식의 성능과 관련해 검색을 해보니, 이 방법은 대량데이터를 옴기기엔 적합하지 않다고 한다.
이렇게 입력할 데이터가 많을 경우 다른 방법을 제시했는데,
1. 배열을 워크시트로 전송
2. ADO 레코드를 워크시트로 전송
3. Windows 클립보드를 이용한 전송
이렇게 3가지 방법이다.
예제코드는 출처로 가보길 바람.
C# 숫자 컬럼값을 액셀 알파벳컬럼값으로 변환
숫자 컬럼 값을 엑셀에서 사용하는 알파벳으로 변환해 봄.
////// 10진수 컬럼 값을 엑셀에서 사용하는 컬럼 값 처럼, 알파벳으로 변환 /// /// ///public string DecToAlphabet(int num) { int rest; //나눗셈 계산에 사용될 나머지 값 string alphabet; //10진수에서 알파벳으로 변환될 값 byte[] asciiA = Encoding.ASCII.GetBytes("A"); // 0=>A rest = num % 26; // A~Z 26자 asciiA[0] += (byte)rest; // num 0일 때 A, num 4일 때 A+4 => E alphabet = Encoding.ASCII.GetString(asciiA); //변환된 알파벳 저장 num = num / 26 -1; // 그 다음 자리의 알파벳 계산을 재귀하기 위해, 받은 수/알파벳수 -1 (0은 A라는 문자값이 있으므로 -1을 기준으로 계산함) if (num > -1) { alphabet=alphabet.Insert(0,DecToAlphabet(num)); //재귀 호출하며 결과를 앞자리에 insert } return alphabet; // 최종값 return } /// /// int input=Int32.Parse(textBox1.Text.ToString()); /// Trace.WriteLine(input+"=> "+ xlsmaker.DecToAlphabet(input)); /// 결과 /// 0=> A /// 27=> AB /// 16383=> XFD //
2012-06-19
C# Find number in String
문자열에서 숫자열을 찾아, 순서대로 List에 저장하는 클래스.
using System.Collections.Generic; namespace automating_system_report { class FindNumberInString { public ListListNumber=new List (); public void FindNumber(string numStr) { int startIndex = 0; // substring할 시작지점 int lengthOfNumber = 0; // substring할 길이 int count=0; // foreach에서 사용할 카운터 bool bThisTurnIsNumeric; // foreach에서 문자가 숫자인지 확인할 bool if (ListNumber != null) // 함수가 이미 호출되어 리스트에 값이 있는 경우 clear { ListNumber.Clear(); } /// numStr += "/"; /// numStr에 "/"를 문자열 끝에 붙였던 이유는, /// 문자열에서 숫자가 나오다 숫자가 아닌 문자가 나오면 그 순간 숫자열을 판단 하는데, /// 문자열 마지막이 숫자로 끝날 경우, List에 저장하지 못하고 foreach문을 끝내게된다.. /// /// 마지막에 if (bThisTurnIsNumeric == true && numStr.Length == (count + 1)) 로 대체함. foreach (char c in numStr) { bThisTurnIsNumeric = (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9'); if (lengthOfNumber == 0) //숫자가 아닌 경우, lengthOfNumber가 0이면 아직 문자열에서 숫자가 발견되지 않은 것, startIndex를 +1 { startIndex = count; } if (bThisTurnIsNumeric == true) //숫자를 찾은 경우, LengthOfNumber를 +1 { lengthOfNumber++; } if ((bThisTurnIsNumeric == false&&lengthOfNumber>0) //숫자열이 끝나고 문자가 시작되면 숫자열을 Substring할 준비가 된 것. || (bThisTurnIsNumeric == true && numStr.Length == (count + 1))) //문자열 마지막에 숫자열이 있을 경우, 다시 foreach를 실행하지 않을 것이므로 지금 저장 { ListNumber.Add(numStr.Substring(startIndex, lengthOfNumber)); lengthOfNumber = 0; // 문자열의 숫자부분 길이를 리셋 } count++; } } } } /* FindNumberInString fnfs = new FindNumberInString(); fnfs.FindNumber("오늘 날짜는 2012년 06월 19일 17:29:00 "); int i = 0; foreach (string s in fnfs.ListNumber) { Trace.WriteLine(i.ToString() + ":" + s); i++; } 결과: 0:2012 1:06 2:19 3:17 4:29 5:00 */ //
Tag
C#
2012-06-15
C# load INI file
사용자의 설정을 기억할 목적으로 ini파일을 사용해 보고자 만들었다.
INI파일을 로드하고, 원하는 항목을 return 하는 기능을 구현했다.
일부러 이번엔 다른 사람의 코드를 먼저 보지않고, 내 나름대로 작성한 후에 봤다.
작성 후, 다른 사람의 소스를 보니 굉장히 간결했다. 아직 가야할 길이 먼 것 같다.
(비교: http://www.codeproject.com/Articles/1966/An-INI-file-handling-class-using-C)
검색된 소스는 GetPrivateProfileString, WritePrivateProfileString라는 API를 사용했다.
using System.Collections.Generic; using System.Collections; using System.IO; using System.Diagnostics; namespace automating_system_report { #region INISTRUCT public class INISTRUCT //INI파일의 한 줄의 내용을 저장할 class. List로 사용함. { public string lvar; // 왼쪽 문자열(상수이름) public string rvar; // 오른쪽 문자열(상수값) public INISTRUCT(string inputLvar, string inputRvar) { lvar = inputLvar; rvar = inputRvar; } } #endregion #region INILoader ////// INI 파일을 읽어 , 데이터 이름과 데이터 값을 listINI 리스트에 저장하는 클래스. /// INILoader를 선언하고, LoadINIFile("filename") 메쏘드를 실행하면, /// INI파일의 값들을 클래스 내의 listINI 리스트에 저장한다. /// public class INILoader { #region globar var private string matchString; //Predicate함수로 전달할 목적으로 전역 변수를 사용함. #endregion #region public member //INISTRUCT의 List public ListlistINI = new List (); #endregion #region public method // INI파일을 로딩, public bool LoadINIFile(string filename) { try { if (File.Exists(filename)) { FileStream fs = File.OpenRead(filename); StreamReader sr = new StreamReader(fs); while (sr.Peek() > -1) // Peek은 문자가 더 이상 없으면 반환 값은 -1 이다. { FillINISTRUCT(sr.ReadLine()); // FillINIStruct로 전역 리스트 listINI에 값을 저장함. } sr.Close(); fs.Close(); return true; } return false; } catch { return false; } } public void SaveINIFile(string filename) { try { /* FileStream fs=new FileStream(filename); * StreamWriter sw=new StreamWriter(fs); * 이렇게 해서 쓰면, 생성된 파일 끝부분에 이전의 쓰레기 값이 남았다...왜 그런지는 알아봐야함. * 이미 있는 파일에 덮어쓰기 할 때, 이전 값이 더 클 경우 쓰레기 값이 남는 것으로 보임. */ StreamWriter sw = new StreamWriter(filename,false, System.Text.Encoding.Unicode); foreach (INISTRUCT i in listINI) { sw.WriteLine(@i.lvar + " = " + @i.rvar); //Trace.WriteLine("sw:"+i.lvar + "," + i.rvar); } sw.Close(); } catch { } } public bool AddINI(string inistr) { INISTRUCT inputINI = StringToINISTRUCT(inistr); for (int i = 0; i < listINI.Count; i++) { if (listINI[i].lvar == inputINI.lvar) //이미 해당 lvar가 있으면 rvar를 수정 { Trace.WriteLine(listINI[i].rvar+"->"+inputINI.rvar); listINI[i].rvar = inputINI.rvar; return true; } } listINI.Add(inputINI); // 기존에 없던 rvar값이면 추가. return true; } public string FindValue(string valueName) { //valueName의 lvar를 찾아 rvar를 return matchString = valueName; try { INISTRUCT pickINI = listINI.Find(MatchingPredicate); // MatchingPredicate의 주석 참조. return pickINI.rvar; //"valueName = matchString = 찾고자 하는 lvar의 이름"으로 찾은 rvar의 값 } catch { return null; // lvar에 valueName없음. } } #endregion #region private method private bool MatchingPredicate(INISTRUCT p) { /// http://msdn.microsoft.com/ko-kr/library/x0b5b5bc.aspx /// MSDN에서는 Predicate 함수를 static으로 선언하고 있어, matchString도 static으로 선언해야 한다... /// Predicate함수와 matchString을 둘 다 static에서 해제 했음. /// 람다식을 이용하면 더 깔끔한(전역변수 남용 없이, predicate 선언 없이..) 구현할 수 있을 것 같다. return (matchString == p.lvar); } /// /// INI파일에서 읽어들인 레코드를 Substring, Trim 하는 함수 /// /// "상수이름 = 값" 형식의 문자열 private void FillINISTRUCT(string tmpiniString) { //Trim된 Rvar와 Lvar를 리스트에 Add() INISTRUCT tmpinistruct = StringToINISTRUCT(tmpiniString); listINI.Add(tmpinistruct); } public INISTRUCT StringToINISTRUCT(string inistr) { int locateEqualMark; // "=" 마크 위치 int locateRvarStart, locateRvarEnd; // rvar substring 위치 string tmpLvar; // lvar를 저장할 임시 변수 string tmpRvar; // rvar를 저장할 임시 변수 //substring 할 위치 locateEqualMark = inistr.IndexOf('='); locateRvarStart = locateEqualMark + 1; locateRvarEnd = inistr.Length - locateEqualMark - 1; //substring if (locateEqualMark == -1) {return null;} // Trim하기 전의 임시변수 tmpLvar = inistr.Substring(0, locateEqualMark); tmpRvar = inistr.Substring(locateRvarStart, locateRvarEnd); //앞뒤 공백제거 " some string" or "some string " => "some string" tmpLvar = tmpLvar.Trim(); tmpRvar = tmpRvar.Trim(); return new INISTRUCT(tmpLvar, tmpRvar); } #endregion } #endregion } //
테스트 코드
#region global var INILoader ini = new INILoader(); #endregion #region const var public const string inifilename = @"C:\windows\win.ini"; #endregion public DlgOption() { InitializeComponent(); ini.LoadINIFile(inifilename); //테스트 foreach foreach (INISTRUCT s in ini.listINI) { string traceString; traceString=string.Format("Trace: \"{0}\", \"{1}\"",s.lvar,s.rvar); Trace.WriteLine(traceString); } Trace.WriteLine("find="+ini.FindValue("mp3")); Trace.WriteLine("find=" + ini.FindValue("MP3")); } /* 테스트 Trace: "CMC", "1" Trace: "MAPI", "1" Trace: "MAPIX", "1" Trace: "MAPIXVER", "1.0.0.1" Trace: "OLEMessaging", "1" find=MPEGVideo <- "MP3" 검색결과 'System.NullReferenceException' 형식의 첫째 예외가 automating_system_report.exe에서 발생했습니다. find= <- 찾는 항목 없음 "mp3" 검색결과... */ //
Tag
C#
피드 구독하기:
글 (Atom)