
Word 문서에서 텍스트 추출은 종종 다른 시나리오에서 수행됩니다. 예를 들어 텍스트를 분석하거나 문서의 특정 섹션을 추출하여 단일 문서로 결합하는 등의 작업이 있습니다. 이 기사에서는 C#에서 프로그래밍 방식으로 Word 문서에서 텍스트를 추출하는 방법을 배웁니다. 또한 단락, 표 등과 같은 특정 요소 간의 콘텐츠를 동적으로 추출하는 방법을 다룹니다.
Word 문서에서 텍스트를 추출하는 C# 라이브러리
Aspose.Words for .NET는 처음부터 MS Word 문서를 만들 수 있는 강력한 라이브러리입니다. 또한 암호화, 변환, 텍스트 추출 등을 위해 기존 Word 문서를 조작할 수 있습니다. 이 라이브러리를 사용하여 Word DOCX 또는 DOC 문서에서 텍스트를 추출합니다. API의 DLL을 다운로드하거나 패키지 관리자 콘솔을 사용하여 NuGet에서 직접 설치할 수 있습니다.
PM> Install-Package Aspose.Words
C#을 사용하여 Word 문서에서 텍스트 추출
MS Word 문서는 단락, 표, 이미지 등을 포함하는 다양한 요소로 구성됩니다. 따라서 텍스트 추출 요구 사항은 시나리오마다 다를 수 있습니다. 예를 들어 단락, 책갈피, 주석 등 사이에서 텍스트를 추출해야 할 수 있습니다.
Word 문서의 각 요소 유형은 노드로 표시됩니다. 따라서 문서를 처리하려면 노드를 가지고 놀아야 합니다. 다양한 시나리오에서 Word 문서에서 텍스트를 추출하는 방법을 시작해 보겠습니다.
C#의 Word 문서에서 텍스트 추출
이 섹션에서는 Word 문서용 C# 텍스트 추출기를 구현하고 텍스트 추출 워크플로는 다음과 같습니다.
- 먼저 텍스트 추출 프로세스에 포함할 노드를 정의합니다.
- 그런 다음 지정된 노드(시작 및 끝 노드 포함 또는 제외) 사이의 콘텐츠를 추출합니다.
- 마지막으로 추출된 노드의 복제본을 사용하여 추출된 콘텐츠로 구성된 새 Word 문서를 만듭니다.
이제 텍스트 추출을 수행하기 위해 노드와 기타 매개변수를 전달할 ExtractContent라는 메서드를 작성해 보겠습니다. 이 방법은 문서를 구문 분석하고 노드를 복제합니다. 다음은 이 메서드에 전달할 매개변수입니다.
- StartNode 및 EndNode는 각각 콘텐츠 추출을 위한 시작 및 종료 지점입니다. 이들은 블록 수준(Paragraph, Table) 또는 인라인 수준(예: Run, FieldStart, BookmarkStart 등) 노드일 수 있습니다.
- 필드를 전달하려면 해당 FieldStart 개체를 전달해야 합니다.
- 책갈피를 전달하려면 BookmarkStart 및 BookmarkEnd 노드를 전달해야 합니다.
- 주석의 경우 CommentRangeStart 및 CommentRangeEnd 노드를 사용해야 합니다.
- IsInclusive는 마커가 추출에 포함되는지 여부를 정의합니다. 이 옵션이 false로 설정되고 동일한 노드 또는 연속 노드가 전달되면 빈 목록이 반환됩니다.
다음은 전달된 노드 간의 콘텐츠를 추출하는 ExtractContent 메서드의 완전한 구현입니다.
public static ArrayList ExtractContent(Node startNode, Node endNode, bool isInclusive)
{
// 먼저 이 메소드에 전달된 노드가 사용하기에 유효한지 확인하십시오.
VerifyParameterNodes(startNode, endNode);
// 추출된 노드를 저장할 목록을 만듭니다.
ArrayList nodes = new ArrayList();
// 필요한 경우 마커 노드를 분할할 수 있도록 이 메서드에 전달된 원래 노드의 기록을 유지합니다.
Node originalStartNode = startNode;
Node originalEndNode = endNode;
// 블록 수준 노드(단락 및 표)를 기반으로 콘텐츠를 추출합니다. 상위 노드를 탐색하여 찾습니다.
// 마커 노드가 인라인인지 여부에 따라 첫 번째 노드와 마지막 노드의 내용을 분할합니다.
while (startNode.ParentNode.NodeType != NodeType.Body)
startNode = startNode.ParentNode;
while (endNode.ParentNode.NodeType != NodeType.Body)
endNode = endNode.ParentNode;
bool isExtracting = true;
bool isStartingNode = true;
bool isEndingNode = false;
// 문서에서 추출하는 현재 노드입니다.
Node currNode = startNode;
// 콘텐츠 추출을 시작합니다. 모든 블록 수준 노드를 처리하고 단락 형식이 유지되도록 필요할 때 특히 첫 번째 노드와 마지막 노드를 분할합니다.
// 메서드는 인라인 노드, 필드, 책갈피 등을 사용하여 추출해야 하므로 일반 추출기보다 약간 복잡합니다.
while (isExtracting)
{
// 현재 노드와 그 자식을 복제하여 복사본을 얻습니다.
Node cloneNode = currNode.Clone(true);
isEndingNode = currNode.Equals(endNode);
if ((isStartingNode || isEndingNode) && cloneNode.IsComposite)
{
// 각 마커를 별도로 처리해야 하므로 대신 별도의 메서드로 전달합니다.
if (isStartingNode)
{
ProcessMarker((CompositeNode)cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
isStartingNode = false;
}
// 조건부는 블록 레벨 시작 및 종료 마커가 동일한 노드일 수 있으므로 분리되어야 합니다.
if (isEndingNode)
{
ProcessMarker((CompositeNode)cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
isExtracting = false;
}
}
else
// 노드는 시작 또는 끝 마커가 아니므로 목록에 복사본을 추가하기만 하면 됩니다.
nodes.Add(cloneNode);
// 다음 노드로 이동하여 압축을 풉니다. 다음 노드가 null이면 나머지 콘텐츠가 다른 섹션에 있음을 의미합니다.
if (currNode.NextSibling == null && isExtracting)
{
// 다음 섹션으로 이동합니다.
Section nextSection = (Section)currNode.GetAncestor(NodeType.Section).NextSibling;
currNode = nextSection.Body.FirstChild;
}
else
{
// 본문의 다음 노드로 이동합니다.
currNode = currNode.NextSibling;
}
}
// 노드 마커 사이의 노드를 반환합니다.
return nodes;
}
아래에 나와 있는 텍스트 추출 작업을 수행하기 위해 ExtractContent 메서드에서 일부 도우미 메서드도 필요합니다.
public static List<Paragraph> ParagraphsByStyleName(Document doc, string styleName)
{
// 지정된 스타일의 단락을 수집하는 배열을 만듭니다.
List<Paragraph> paragraphsWithStyle = new List<Paragraph>();
NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);
// 지정된 스타일을 가진 단락을 찾으려면 모든 단락을 살펴보십시오.
foreach (Paragraph paragraph in paragraphs)
{
if (paragraph.ParagraphFormat.Style.Name == styleName)
paragraphsWithStyle.Add(paragraph);
}
return paragraphsWithStyle;
}
private static void VerifyParameterNodes(Node startNode, Node endNode)
{
// 이러한 검사가 수행되는 순서가 중요합니다.
if (startNode == null)
throw new ArgumentException("Start node cannot be null");
if (endNode == null)
throw new ArgumentException("End node cannot be null");
if (!startNode.Document.Equals(endNode.Document))
throw new ArgumentException("Start node and end node must belong to the same document");
if (startNode.GetAncestor(NodeType.Body) == null || endNode.GetAncestor(NodeType.Body) == null)
throw new ArgumentException("Start node and end node must be a child or descendant of a body");
// 끝 노드가 DOM 트리의 시작 노드 뒤에 있는지 확인하십시오.
// 먼저 다른 섹션에 있는지 확인한 다음 동일한 섹션의 본문에서 위치를 확인하지 않으면 속해 있습니다.
Section startSection = (Section)startNode.GetAncestor(NodeType.Section);
Section endSection = (Section)endNode.GetAncestor(NodeType.Section);
int startIndex = startSection.ParentNode.IndexOf(startSection);
int endIndex = endSection.ParentNode.IndexOf(endSection);
if (startIndex == endIndex)
{
if (startSection.Body.IndexOf(startNode) > endSection.Body.IndexOf(endNode))
throw new ArgumentException("The end node must be after the start node in the body");
}
else if (startIndex > endIndex)
throw new ArgumentException("The section of end node must be after the section start node");
}
private static bool IsInline(Node node)
{
// 노드가 단락 또는 테이블 노드의 후손이고 단락 또는 테이블이 아닌지 테스트하고 단락의 후손인 주석 클래스 내부의 단락이 가능합니다.
return ((node.GetAncestor(NodeType.Paragraph) != null || node.GetAncestor(NodeType.Table) != null) && !(node.NodeType == NodeType.Paragraph || node.NodeType == NodeType.Table));
}
private static void ProcessMarker(CompositeNode cloneNode, ArrayList nodes, Node node, bool isInclusive, bool isStartMarker, bool isEndMarker)
{
// 블록 수준 노드를 다루는 경우 포함해야 하는지 확인하고 목록에 추가합니다.
if (!IsInline(node))
{
// 마커가 동일한 노드인 경우 노드를 두 번 추가하지 마십시오.
if (!(isStartMarker && isEndMarker))
{
if (isInclusive)
nodes.Add(cloneNode);
}
return;
}
// 마커가 FieldStart 노드인 경우 포함 여부를 확인합니다.
// 단순화를 위해 FieldStart와 FieldEnd가 같은 단락에 나타난다고 가정합니다.
if (node.NodeType == NodeType.FieldStart)
{
// 마커가 시작 노드이고 포함되지 않은 경우 필드 끝으로 건너뜁니다.
// 마커가 끝 노드이고 포함되어야 하는 경우 끝 필드로 이동하여 필드가 제거되지 않도록 합니다.
if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive))
{
while (node.NextSibling != null && node.NodeType != NodeType.FieldEnd)
node = node.NextSibling;
}
}
// 두 마커 중 하나가 주석의 일부인 경우 주석 자체를 포함하려면 포인터를 주석으로 이동해야 합니다.
// CommentRangeEnd 노드 뒤에 노드가 있습니다.
if (node.NodeType == NodeType.CommentRangeEnd)
{
while (node.NextSibling != null && node.NodeType != NodeType.Comment)
node = node.NextSibling;
}
// 인덱스로 복제된 노드에서 해당 노드를 찾아 반환합니다.
// 시작 및 끝 노드가 동일한 경우 일부 하위 노드가 이미 제거되었을 수 있습니다. 빼다
// 올바른 인덱스를 얻기 위한 차이점.
int indexDiff = node.ParentNode.ChildNodes.Count - cloneNode.ChildNodes.Count;
// 자식 노드 수가 동일합니다.
if (indexDiff == 0)
node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node)];
else
node = cloneNode.ChildNodes[node.ParentNode.IndexOf(node) - indexDiff];
// 마커까지/에서 노드를 제거합니다.
bool isSkip = false;
bool isProcessing = true;
bool isRemoving = isStartMarker;
Node nextNode = cloneNode.FirstChild;
while (isProcessing && nextNode != null)
{
Node currentNode = nextNode;
isSkip = false;
if (currentNode.Equals(node))
{
if (isStartMarker)
{
isProcessing = false;
if (isInclusive)
isRemoving = false;
}
else
{
isRemoving = true;
if (isInclusive)
isSkip = true;
}
}
nextNode = nextNode.NextSibling;
if (isRemoving && !isSkip)
currentNode.Remove();
}
// 복합 노드를 처리한 후 비어 있을 수 있습니다. 포함하지 않은 경우.
if (!(isStartMarker && isEndMarker))
{
if (cloneNode.HasChildNodes)
nodes.Add(cloneNode);
}
}
public static Document GenerateDocument(Document srcDoc, ArrayList nodes)
{
// 빈 문서를 만듭니다.
Document dstDoc = new Document();
// 빈 문서에서 첫 번째 단락을 제거합니다.
dstDoc.FirstSection.Body.RemoveAllChildren();
// 목록의 각 노드를 새 문서로 가져옵니다. 노드의 원래 형식을 유지합니다.
NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KeepSourceFormatting);
foreach (Node node in nodes)
{
Node importNode = importer.ImportNode(node, true);
dstDoc.FirstSection.Body.AppendChild(importNode);
}
// 생성된 문서를 반환합니다.
return dstDoc;
}
이제 이러한 방법을 활용하고 Word 문서에서 텍스트를 추출할 준비가 되었습니다.
Word 문서에서 단락 사이의 텍스트 추출
Word DOCX 문서에서 두 단락 사이의 내용을 추출하는 방법을 살펴보겠습니다. 다음은 C#에서 이 작업을 수행하는 단계입니다.
- 먼저 Document 클래스를 사용하여 Word 문서를 로드합니다.
- Document.FirstSection.Body.GetChild(NodeType.PARAGRAPH, int, boolean) 메서드를 사용하여 시작 및 끝 단락의 참조를 두 개체로 가져옵니다.
- ExtractContent(startPara, endPara, True) 메서드를 호출하여 노드를 객체로 추출합니다.
- GenerateDocument(Document, extractNodes) 도우미 메서드를 호출하여 추출된 내용으로 구성된 문서를 만듭니다.
- 마지막으로 Document.Save(string) 메서드를 사용하여 반환된 문서를 저장합니다.
다음 코드 샘플은 C#의 Word 문서에서 7번째와 11번째 단락 사이의 텍스트를 추출하는 방법을 보여줍니다.
// Word 문서 로드
Document doc = new Document("document.docx");
// 노드 수집(GetChild 메서드는 0부터 시작하는 인덱스 사용)
Paragraph startPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 6, true);
Paragraph endPara = (Paragraph)doc.FirstSection.Body.GetChild(NodeType.Paragraph, 10, true);
// 문서에서 이러한 노드 사이의 콘텐츠를 추출합니다. 추출에 이러한 마커를 포함합니다.
ArrayList extractedNodes = ExtractContent(startPara, endPara, true);
// 콘텐츠를 새 문서에 삽입하고 디스크에 저장합니다.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");
Word 문서에서 서로 다른 유형의 노드 간에 텍스트 추출
다른 유형의 노드 간에 콘텐츠를 추출할 수도 있습니다. 데모를 위해 단락과 표 사이의 콘텐츠를 추출하여 새 Word 문서에 저장해 보겠습니다. 다음은 이 작업을 수행하는 단계입니다.
- Document 클래스를 사용하여 Word 문서를 로드합니다.
- Document.FirstSection.Body.GetChild(NodeType, int, boolean) 메서드를 사용하여 시작 및 끝 노드의 참조를 두 개체로 가져옵니다.
- ExtractContent(startPara, endPara, True) 메서드를 호출하여 노드를 객체로 추출합니다.
- GenerateDocument(Document, extractNodes) 도우미 메서드를 호출하여 추출된 내용으로 구성된 문서를 만듭니다.
- Document.Save(string) 메서드를 사용하여 반환된 문서를 저장합니다.
다음 코드 샘플은 C#에서 단락과 표 사이의 텍스트를 추출하는 방법을 보여줍니다.
// Word 문서 로드
Document doc = new Document("document.docx");
Paragraph startPara = (Paragraph)doc.LastSection.GetChild(NodeType.Paragraph, 2, true);
Table endTable = (Table)doc.LastSection.GetChild(NodeType.Table, 0, true);
// 문서에서 이러한 노드 사이의 콘텐츠를 추출합니다. 추출에 이러한 마커를 포함합니다.
ArrayList extractedNodes = ExtractContent(startPara, endTable, true);
// 콘텐츠를 새 문서에 삽입하고 디스크에 저장합니다.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");
스타일을 기반으로 단락 사이의 텍스트 추출
이제 스타일을 기반으로 단락 사이의 내용을 추출하는 방법을 알아보겠습니다. 데모를 위해 Word 문서의 첫 번째 “제목 1"과 첫 번째 “제목 3” 사이의 내용을 추출합니다. 다음 단계는 C#에서 이를 달성하는 방법을 보여줍니다.
- 먼저 Document 클래스를 사용하여 Word 문서를 로드합니다.
- 그런 다음 ParagraphsByStyleName(Document, “Heading 1”) 도우미 메서드를 사용하여 단락을 개체로 추출합니다.
- ParagraphsByStyleName(Document, “Heading 3”) 도우미 메서드를 사용하여 단락을 다른 개체로 추출합니다.
- ExtractContent(startPara, endPara, True) 메서드를 호출하고 두 단락 배열의 첫 번째 요소를 첫 번째 및 두 번째 매개변수로 전달합니다.
- GenerateDocument(Document, extractNodes) 도우미 메서드를 호출하여 추출된 내용으로 구성된 문서를 만듭니다.
- 마지막으로 Document.Save(string) 메서드를 사용하여 반환된 문서를 저장합니다.
다음 코드 샘플은 스타일을 기반으로 단락 사이의 콘텐츠를 추출하는 방법을 보여줍니다.
// Word 문서 로드
Document doc = new Document("document.docx");
// 각 제목 스타일을 사용하여 단락 목록을 수집합니다.
List<Paragraph> parasStyleHeading1 = ParagraphsByStyleName(doc, "Heading 1");
List<Paragraph> parasStyleHeading3 = ParagraphsByStyleName(doc, "Heading 3");
// 해당 스타일이 있는 단락의 첫 번째 인스턴스를 사용합니다.
Node startPara1 = (Node)parasStyleHeading1[0];
Node endPara1 = (Node)parasStyleHeading3[0];
// 문서에서 이러한 노드 사이의 콘텐츠를 추출합니다. 추출에 이러한 마커를 포함하지 마십시오.
ArrayList extractedNodes = ExtractContent(startPara1, endPara1, false);
// 콘텐츠를 새 문서에 삽입하고 디스크에 저장합니다.
Document dstDoc = GenerateDocument(doc, extractedNodes);
dstDoc.Save("output.docx");
더 읽기
이 문서 문서를 사용하여 Word 문서에서 텍스트를 추출하는 다른 시나리오를 탐색할 수 있습니다.
무료 API 라이선스 받기
평가 제한 없이 Aspose.Words for .NET을 사용할 수 있는 임시 라이선스를 얻을 수 있습니다.
결론
이 기사에서는 C#을 사용하여 MS Word 문서에서 텍스트를 추출하는 방법을 배웠습니다. 또한 프로그래밍 방식으로 Word 문서에서 유사하거나 다른 유형의 노드 간에 콘텐츠를 추출하는 방법을 살펴보았습니다. 따라서 C#에서 자신만의 MS Word 텍스트 추출기를 구축할 수 있습니다. 게다가 문서를 사용하여 .NET용 Aspose.Words의 다른 기능을 탐색할 수 있습니다. 질문이 있는 경우 포럼을 통해 언제든지 알려주십시오.
또한보십시오
팁: Aspose PowerPoint에서 Word로 변환기는 인기 있는 프레젠테이션을 Word 문서로 변환하는 과정을 보여주기 때문에 확인하는 것이 좋습니다.