목표 :

Excel 파일을 가져 와서 특정 열의 행을 읽고 싶습니다. 이를 위해 ExcelDataReader를 사용합니다. 저는 ExcelDataReader를 사용하는 ExcelData라는 저수준 클래스를 구현했으며 “.xls인지 확인하는 것과 같은 작업을 수행합니다. “of”.xslx “파일 (또는 완전히 관련없는 것!) 등. 해당 클래스 위에 Excel에서 원하는 특정 열을 가져 오는 ReadInData 클래스를 만들었습니다. 시트.

주요 관심사 :

  • 내 메인 프로그램의 캐치 목록
  • 예외 던지기
  • 전체 구성 코드 및 코드 품질
  • ExcelData 클래스를 ReadInData 내에 캡슐화해야합니까, 아니면 그대로 유지해야합니까? ?
  • isFirstRowAsColumnNames 매개 변수 전달

이것은 회사 코드이므로 부부의 이름을 변경했습니다. 그래서 나는 그들이 “최고의 이름이 아니라는 것을 알고 있습니다.

내 코드의 진입 점 :

class Program { static void Main(string[] args) { try { ReadInData readInData = new ReadInData(@"C:\SC.xlsx", "sc_2014"); IEnumerable<Recipient> recipients = readInData.GetData(); } catch (FileNotFoundException ex) { Console.WriteLine(ex.Message); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); } catch (WorksheetDoesNotExistException ex) { Console.WriteLine(ex.Message); } catch (FileToBeProcessedIsNotInTheCorrectFormatException ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); } } 

코드는 새로운 ReadInData 클래스를 만듭니다. s 경로, 파일 이름 및 내가 읽고 자하는 Excel 시트의 이름입니다.

여기서 우려 사항 : 해당 파일 내에서 이러한 내용을 전달해도됩니까?

ReadInData 클래스 :

public class ReadInData { private string path; private string worksheetName; public ReadInData(string path, string worksheetName) { this.path = path; this.worksheetName = worksheetName; } public IEnumerable<Recipient> GetData(bool isFirstRowAsColumnNames = true) { var excelData = new ExcelData(path); var dataRows = excelData.GetData(worksheetName, isFirstRowAsColumnNames); return dataRows.Select(dataRow => new Recipient() { Municipality = dataRow["Municipality"].ToString(), Sexe = dataRow["Sexe"].ToString(), LivingArea = dataRow["LivingArea"].ToString() }).ToList(); } } 

기본적으로 ExcelData 클래스, 다소 높은 수준으로 보였기 때문입니다.

ExcelData 클래스 :

public class ExcelData { private string path; public ExcelData(string path) { this.path = path; } private IExcelDataReader GetExcelDataReader(bool isFirstRowAsColumnNames) { using (FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read)) { IExcelDataReader dataReader; if (path.EndsWith(".xls")) { dataReader = ExcelReaderFactory.CreateBinaryReader(fileStream); } else if (path.EndsWith(".xlsx")) { dataReader = ExcelReaderFactory.CreateOpenXmlReader(fileStream); } else { //Throw exception for things you cannot correct throw new FileToBeProcessedIsNotInTheCorrectFormatException("The file to be processed is not an Excel file"); } dataReader.IsFirstRowAsColumnNames = isFirstRowAsColumnNames; return dataReader; } } private DataSet GetExcelDataAsDataSet(bool isFirstRowAsColumnNames) { return GetExcelDataReader(isFirstRowAsColumnNames).AsDataSet(); } private DataTable GetExcelWorkSheet(string workSheetName, bool isFirstRowAsColumnNames) { DataSet dataSet = GetExcelDataAsDataSet(isFirstRowAsColumnNames); DataTable workSheet = dataSet.Tables[workSheetName]; if (workSheet == null) { throw new WorksheetDoesNotExistException(string.Format("The worksheet {0} does not exist, has an incorrect name, or does not have any data in the worksheet", workSheetName)); } return workSheet; } public IEnumerable<DataRow> GetData(string workSheetName, bool isFirstRowAsColumnNames = true) { DataTable workSheet = GetExcelWorkSheet(workSheetName, isFirstRowAsColumnNames); IEnumerable<DataRow> rows = from DataRow row in workSheet.Rows select row; return rows; } } 

그리고 마지막으로 Recipient 클래스 (특별한 작업 없음) :

namespace ConsoleApplicationForTestingPurposes { public class Recipient { public string Municipality { get; set; } public string Sexe { get; set; } public string LivingArea { get; set; } } } 

예외 클래스는 Exception에서 상속되며 Exception에 메시지를 전달합니다.

Comments

  • 이 코드는 모든 Excel 파일에서 작동하지 않습니다 … 다른 EXCEL 파일 엔딩도 있음을 명심하십시오. ".xlsm"".xlsb" 및 템플릿 유형 …
  • @ Vogel612 감사합니다. 참고하세요!
  • 도움이되었습니다. 우리는 방금 OpenXml을 사용한 코드가 있었는데, 누군가가 .xls를 처음 사용하려고 시도했을 때 예외가 발생했습니다 (첫 구현 후 몇 년 후). 그런 다음 첫 번째 판독기가 유효하지 않은 후에 바이너리 판독기를 시도했기 때문에 ‘이를 수정하려는 첫 번째 시도가 작동하지 않았습니다.하지만 그로 인해 스트림이 망가졌습니다. 먼저 확장 프로그램을 확인하여 수정했습니다.

답변

캐치 체인 단순화

static void Main(string[] args) { try { ReadInData readInData = new ReadInData(@"C:\SC.xlsx", "sc_2014"); IEnumerable<Recipient> recipients = readInData.GetData(); } catch (Exception ex) { if(!(ex is FileNotFoundException || ex is ArgumentException || ex is FileToBeProcessedIsNotInTheCorrectFormatException)) throw; Console.WriteLine(ex.Message); } Console.Write(Press any key to continue...); Console.ReadKey(true); } 

ReadInData가 비 정적 클래스가 될 이유가 없습니다. 경로와 워크 시트 이름을 기억하고 있다는 사실을 이용하지 않고 통합 문서에 대한 일종의 열린 연결을 유지하지 않습니다. 그리고 항상 다음과 같은 변수를 제거하여 코드를 더 복잡하게 보이게 만들 수 있습니다. 한 번만 사용됩니다 (선택 사항). 어쨌든 IEnumerable<T>를 반환하므로 ToList()이를 수행 할 이유가 없습니다.

public static class ReadInData { public static IEnumerable<Recipient> GetData(string path, string worksheetName, bool isFirstRowAsColumnNames = true) { return new ExcelData(path).GetData(worksheetName, isFirstRowAsColumnNames) .Select(dataRow => new Recipient() { Municipality = dataRow["Municipality"].ToString(), Sexe = dataRow["Sexe"].ToString(), LivingArea = dataRow["LivingArea"].ToString() }); } } 

ExcelData 클래스에 대해 이와 동일한 작업을 수행 할 수 있습니다. 즉 .. 그럴 필요는 없지만 OO를 많이 활용하지 않기 때문에 OO로 만들 필요가 있습니다.


그렇지 않으면 캡슐화하는 것보다 ExcelData 클래스를 사용하는 사람을 원하거나 필요로합니다. 그러나 언젠가는이 엑셀 리더를 다시 사용할 수 있기를 원할 것입니다.이 경우이 메서드를 정적 ExcelHelperClass로 옮길 것입니다. 그리고 첫 번째 수업 ReadInData 원래 프로그램에서 메소드를 만들 것입니다.

private static IExcelDataReader GetExcelDataReader(string path, bool isFirstRowAsColumnNames) { using (FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read)) { IExcelDataReader dataReader; if (path.EndsWith(".xls")) dataReader = ExcelReaderFactory.CreateBinaryReader(fileStream); else if (path.EndsWith(".xlsx")) dataReader = ExcelReaderFactory.CreateOpenXmlReader(fileStream); else throw new FileToBeProcessedIsNotInTheCorrectFormatException("The file to be processed is not an Excel file"); dataReader.IsFirstRowAsColumnNames = isFirstRowAsColumnNames; return dataReader; } } private static DataSet GetExcelDataAsDataSet(string path, bool isFirstRowAsColumnNames) { return GetExcelDataReader(path, isFirstRowAsColumnNames).AsDataSet(); } private static DataTable GetExcelWorkSheet(string path, string workSheetName, bool isFirstRowAsColumnNames) { DataTable workSheet = GetExcelDataAsDataSet(path, isFirstRowAsColumnNames).Tables[workSheetName]; if (workSheet == null) throw new WorksheetDoesNotExistException(string.Format("The worksheet {0} does not exist, has an incorrect name, or does not have any data in the worksheet", workSheetName)); return workSheet; } private static IEnumerable<DataRow> GetData(string path, string workSheetName, bool isFirstRowAsColumnNames = true) { return from DataRow row in GetExcelWorkSheet(path, workSheetName, isFirstRowAsColumnNames).Rows select row; } 

As 주석에 언급 된대로 모든 Excel 파일 형식을 고려하는 것은 아닙니다.


bool isFirstRowAsColumnNames가 아니라면 실제로 진행하는 것이 좋습니다. 전체 OOP를 사용하고 구성 할 때 통합 문서를로드하고 실제로 경로 외에 내부 데이터를 포함하여 OO 디자인을 활용합니다. isFirstRowAsColumnNames, 매우 편리합니다.

댓글

  • 먼저, 시간을 내 주셔서 감사합니다. 함께 내 코드에 대해 개선 할 수있는 점이 있습니다. 몇 가지 질문이 있습니다. 첫 번째 질문은 ‘ ” 그리고 첫 번째 클래스 ReadInData는 원래 프로그램에서 메소드를 만들 것입니다. 두 번째로 저는 ‘ 마지막 문단의 첫 문장에서 무엇을 의미했는지 잘 모르겠습니다.isFirstRowAsColumnNames 매개 변수가 없으면 ‘ 내 코드를 원래대로 유지할 수 있다는 뜻인가요? 그렇다면 왜 그것에 의존할까요?

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다