استخراج نص من مستندات MS Word في Java

غالبًا ما يتم إجراء استخراج النص من مستندات Word في سيناريوهات مختلفة. على سبيل المثال ، لتحليل النص ، لاستخراج أقسام معينة من المستند ودمجها في مستند واحد ، وهكذا. في هذه المقالة ، ستتعلم كيفية استخراج نص من مستندات Word برمجيًا في Java. علاوة على ذلك ، سوف نتناول كيفية استخراج المحتوى بين عناصر محددة مثل الفقرات والجداول وما إلى ذلك بشكل ديناميكي.

مكتبة Java لاستخراج النص من مستندات Word

Aspose.Words for Java هي مكتبة قوية تسمح لك بإنشاء مستندات MS Word من البداية. علاوة على ذلك ، فإنه يتيح لك معالجة مستندات Word الحالية للتشفير والتحويل واستخراج النص وما إلى ذلك. سنستخدم هذه المكتبة لاستخراج النص من مستندات Word DOCX أو DOC. يمكنك تنزيل JAR الخاص بواجهة برمجة التطبيقات أو تثبيته باستخدام تكوينات Maven التالية.

<repository>
    <id>AsposeJavaAPI</id>
    <name>Aspose Java API</name>
    <url>https://repository.aspose.com/repo/</url>
</repository>
<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>22.6</version>
    <type>pom</type>
</dependency>

استخراج النص في Word DOC / DOCX في Java

يتكون مستند MS Word من عناصر مختلفة ، والتي تشمل فقرات وجداول وصور وما إلى ذلك. لذلك ، يمكن أن تختلف متطلبات استخراج النص من سيناريو إلى آخر. على سبيل المثال ، قد تحتاج إلى استخراج نص بين الفقرات والإشارات المرجعية والتعليقات وما إلى ذلك.

يتم تمثيل كل نوع من العناصر في Word DOC / DOCX كعقدة. لذلك ، لمعالجة مستند ، سيتعين عليك اللعب بالعقد. لذلك دعونا نبدأ ونرى كيفية استخراج النص من مستندات Word في سيناريوهات مختلفة.

استخراج نص من مستند Word DOC في Java

في هذا القسم ، سنقوم بتنفيذ مستخرج نص Java لمستندات Word وسيكون سير العمل لاستخراج النص كما يلي:

  • أولاً ، سنحدد العقد التي نريد تضمينها في عملية استخراج النص.
  • بعد ذلك ، سنقوم باستخراج المحتوى بين العقد المحددة (بما في ذلك أو استبعاد عقد البداية والنهاية).
  • أخيرًا ، سنستخدم استنساخ العقد المستخرجة ، على سبيل المثال لإنشاء مستند Word جديد يتكون من محتوى مستخرج.

لنكتب الآن عملية باسم extractContent سنمرر إليها العقد وبعض المعلمات الأخرى لإجراء استخراج النص. ستعمل هذه الطريقة على تحليل المستند واستنساخ العقد. فيما يلي المعلمات التي سنمررها إلى هذه الطريقة.

  1. startNode و endNode كنقطتي بداية ونهاية لاستخراج المحتوى ، على التوالي. يمكن أن تكون هذه العقد على مستوى الكتلة (فقرة ، جدول) أو مستوى مضمن (على سبيل المثال ، Run ، FieldStart ، BookmarkStart وما إلى ذلك).
    1. لتمرير حقل ، يجب عليك تمرير كائن FieldStart المقابل.
    2. لتمرير الإشارات المرجعية ، يجب تمرير عقد BookmarkStart و BookmarkEnd.
    3. للتعليقات ، يجب استخدام عقدتي CommentRangeStart و CommentRangeEnd.
  2. يحدد isInclusive ما إذا كانت العلامات مضمنة في الاستخراج أم لا. إذا تم تعيين هذا الخيار على false وتم تمرير نفس العقدة أو العقد المتتالية ، فسيتم إرجاع قائمة فارغة.

فيما يلي التنفيذ الكامل لطريقة extractContent التي تستخرج المحتوى بين العقد التي تم تمريرها.

// للحصول على أمثلة وملفات بيانات كاملة ، يرجى الانتقال إلى https://github.com/aspose-words/Aspose.Words-for-Java
public static ArrayList extractContent(Node startNode, Node endNode, boolean isInclusive) throws Exception {
    // تحقق أولاً من أن العقد التي تم تمريرها إلى هذه الطريقة صالحة للاستخدام.
    verifyParameterNodes(startNode, endNode);

    // قم بإنشاء قائمة لتخزين العقد المستخرجة.
    ArrayList nodes = new ArrayList();

    // احتفظ بسجل للعقد الأصلية التي تم تمريرها إلى هذه الطريقة حتى نتمكن من تقسيم عقد التحديد إذا لزم الأمر.
    Node originalStartNode = startNode;
    Node originalEndNode = endNode;

    // استخراج المحتوى بناءً على عقد مستوى الكتلة (الفقرات والجداول).اجتياز العقد الأصلية للعثور عليها.
    // سنقوم بتقسيم محتوى العقد الأولى والأخيرة اعتمادًا على ما إذا كانت عُقد التحديد مضمنة
    while (startNode.getParentNode().getNodeType() != NodeType.BODY)
        startNode = startNode.getParentNode();

    while (endNode.getParentNode().getNodeType() != NodeType.BODY)
        endNode = endNode.getParentNode();

    boolean isExtracting = true;
    boolean isStartingNode = true;
    boolean isEndingNode;
    // العقدة الحالية التي نستخرجها من المستند.
    Node currNode = startNode;

    // ابدأ في استخراج المحتوى. قم بمعالجة جميع عقد مستوى الكتلة وقم على وجه التحديد بتقسيم العقد الأولى والأخيرة عند الحاجة لذلك يتم الاحتفاظ بتنسيق الفقرة.
    // الطريقة أكثر تعقيدًا من المستخرج العادي حيث نحتاج إلى الاستخراج باستخدام العقد المضمنة والحقول والإشارات المرجعية وما إلى ذلك لجعلها مفيدة حقًا.
    while (isExtracting) {
        // استنساخ العقدة الحالية وتوابعها للحصول على نسخة.
        /*System.out.println(currNode.getNodeType());
        if(currNode.getNodeType() == NodeType.EDITABLE_RANGE_START
                || currNode.getNodeType() == NodeType.EDITABLE_RANGE_END)
        {
            currNode = currNode.nextPreOrder(currNode.getDocument());
        }*/
        System.out.println(currNode);
        System.out.println(endNode);

        CompositeNode cloneNode = null;
        /// cloneNode = (CompositeNode) CurrNode.deepClone (صحيح) ؛

        Node inlineNode = null;
        if(currNode.isComposite())
        {
            cloneNode = (CompositeNode) currNode.deepClone(true);
        }
        else
        {
            if(currNode.getNodeType() == NodeType.BOOKMARK_END)
            {
                Paragraph paragraph = new Paragraph(currNode.getDocument());
                paragraph.getChildNodes().add(currNode.deepClone(true));
                cloneNode = (CompositeNode)paragraph.deepClone(true);
            }
        }

        isEndingNode = currNode.equals(endNode);

        if (isStartingNode || isEndingNode) {
            // نحتاج إلى معالجة كل علامة على حدة ، لذا قم بتمريرها إلى طريقة منفصلة بدلاً من ذلك.
            if (isStartingNode) {
                processMarker(cloneNode, nodes, originalStartNode, isInclusive, isStartingNode, isEndingNode);
                isStartingNode = false;
            }

            // يجب أن يكون الشرطي منفصلاً لأن علامات بداية ونهاية مستوى الكتلة ربما تكون نفس العقدة.
            if (isEndingNode) {
                processMarker(cloneNode, nodes, originalEndNode, isInclusive, isStartingNode, isEndingNode);
                isExtracting = false;
            }
        } else
            // العقدة ليست علامة بداية أو نهاية ، ما عليك سوى إضافة النسخة إلى القائمة.
            nodes.add(cloneNode);

        // انتقل إلى العقدة التالية واستخرجها. إذا كانت العقدة التالية خالية ، فهذا يعني أن باقي المحتوى موجود في قسم مختلف.
        if (currNode.getNextSibling() == null && isExtracting) {
            // انتقل إلى القسم التالي.
            Section nextSection = (Section) currNode.getAncestor(NodeType.SECTION).getNextSibling();
            currNode = nextSection.getBody().getFirstChild();
        } else {
            // انتقل إلى العقدة التالية في الجسم.
            currNode = currNode.getNextSibling();
        }
    }

    // أعد العقد بين علامات العقدة.
    return nodes;
}

بعض الطرق المساعدة مطلوبة أيضًا من خلال طريقة extractContent لإنجاز عملية استخراج النص ، والمذكورة أدناه.

/**
 * يتحقق من صحة معلمات الإدخال ويمكن استخدامها. يلقي استثناء
 * إذا كان هناك أي مشكلة.
 */
private static void verifyParameterNodes(Node startNode, Node endNode) throws Exception {
	// الترتيب الذي تتم به هذه الفحوصات مهم.
	if (startNode == null)
		throw new IllegalArgumentException("Start node cannot be null");
	if (endNode == null)
		throw new IllegalArgumentException("End node cannot be null");

	if (!startNode.getDocument().equals(endNode.getDocument()))
		throw new IllegalArgumentException("Start node and end node must belong to the same document");

	if (startNode.getAncestor(NodeType.BODY) == null || endNode.getAncestor(NodeType.BODY) == null)
		throw new IllegalArgumentException("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.getParentNode().indexOf(startSection);
	int endIndex = endSection.getParentNode().indexOf(endSection);

	if (startIndex == endIndex) {
		if (startSection.getBody().indexOf(startNode) > endSection.getBody().indexOf(endNode))
			throw new IllegalArgumentException("The end node must be after the start node in the body");
	} else if (startIndex > endIndex)
		throw new IllegalArgumentException("The section of end node must be after the section start node");
}

/**
 * للتحقق مما إذا كانت العقدة التي تم تمريرها هي عقدة مضمنة.
 */
private static boolean isInline(Node node) throws Exception {
	// اختبر ما إذا كانت العقدة تابعة لفقرة أو عقدة جدول وليست أيضًا
	// فقرة أو جدول فقرة داخل فئة التعليق التي تعتبر مقتطعة من
	// الباراراف ممكن.
	return ((node.getAncestor(NodeType.PARAGRAPH) != null || node.getAncestor(NodeType.TABLE) != null)
			&& !(node.getNodeType() == NodeType.PARAGRAPH || node.getNodeType() == NodeType.TABLE));
}

/**
 * يزيل المحتوى قبل أو بعد العلامة في العقدة المستنسخة اعتمادًا
 * على نوع العلامة.
 */
private static void processMarker(CompositeNode cloneNode, ArrayList nodes, Node node, boolean isInclusive,
		boolean isStartMarker, boolean isEndMarker) throws Exception {
	// إذا كنا نتعامل مع عقدة على مستوى الكتلة ، فما عليك سوى معرفة ما إذا كان يجب تضمينها
	// وإضافته إلى القائمة.
	if (!isInline(node)) {
		// لا تقم بإضافة العقدة مرتين إذا كانت العلامات هي نفس العقدة
		if (!(isStartMarker && isEndMarker)) {
			if (isInclusive)
				nodes.add(cloneNode);
		}
		return;
	}

	// إذا كانت العلامة عبارة عن عقدة FieldStart ، تحقق مما إذا كان سيتم تضمينها أم لا.
	// نحن نفترض البساطة أن FieldStart و FieldEnd يظهران في نفس الشيء
	// فقرة.
	if (node.getNodeType() == NodeType.FIELD_START) {
		// إذا كانت العلامة عبارة عن عقدة بداية ولم يتم تضمينها ، فانتقل إلى نهاية
		// الميدان.
		// إذا كانت العلامة عبارة عن عقدة نهاية ويجب تضمينها ، فانتقل إلى النهاية
		// لذلك لن يتم إزالة الحقل.
		if ((isStartMarker && !isInclusive) || (!isStartMarker && isInclusive)) {
			while (node.getNextSibling() != null && node.getNodeType() != NodeType.FIELD_END)
				node = node.getNextSibling();

		}
	}

	// إذا كانت أي من العلامة جزءًا من تعليق ، فعلينا تضمين التعليق نفسه
	// تحتاج إلى تحريك المؤشر إلى الأمام إلى التعليق
	// تم العثور على العقدة بعد العقدة CommentRangeEnd.
	if (node.getNodeType() == NodeType.COMMENT_RANGE_END) {
		while (node.getNextSibling() != null && node.getNodeType() != NodeType.COMMENT)
			node = node.getNextSibling();

	}

	// ابحث عن العقدة المقابلة في العقدة المستنسخة بالفهرس وأعدها.
	// إذا كانت عقدة البداية والنهاية هي نفسها قد تكون لدى بعض العقد الفرعية بالفعل
	// تم إزالة. اطرح
	// الفرق للحصول على المؤشر الصحيح.
	int indexDiff = node.getParentNode().getChildNodes().getCount() - cloneNode.getChildNodes().getCount();

	// عدد العقدة الفرعية متطابق.
	if (indexDiff == 0)
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node));
	else
		node = cloneNode.getChildNodes().get(node.getParentNode().indexOf(node) - indexDiff);

	// قم بإزالة العقد حتى / من العلامة.
	boolean isSkip;
	boolean isProcessing = true;
	boolean isRemoving = isStartMarker;
	Node nextNode = cloneNode.getFirstChild();

	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.getNextSibling();
		if (isRemoving && !isSkip)
			currentNode.remove();
	}

	// بعد معالجة العقدة المركبة قد تصبح فارغة. إذا لم يتضمن
	// هو - هي.
	if (!(isStartMarker && isEndMarker)) {
		if (cloneNode.hasChildNodes())
			nodes.add(cloneNode);
	}
}

public static Document generateDocument(Document srcDoc, ArrayList nodes) throws Exception {

	// قم بإنشاء مستند فارغ.
	Document dstDoc = new Document();
	// أزل الفقرة الأولى من المستند الفارغ.
	dstDoc.getFirstSection().getBody().removeAllChildren();

	// قم باستيراد كل عقدة من القائمة إلى المستند الجديد. احتفظ بالأصل
	// تنسيق العقدة.
	NodeImporter importer = new NodeImporter(srcDoc, dstDoc, ImportFormatMode.KEEP_SOURCE_FORMATTING);

	for (Node node : (Iterable<Node>) nodes) {
		Node importNode = importer.importNode(node, true);
		dstDoc.getFirstSection().getBody().appendChild(importNode);
	}

	// إرجاع المستند الذي تم إنشاؤه.
	return dstDoc;
}

نحن الآن جاهزون لاستخدام هذه الأساليب واستخراج النص من مستند Word.

Java Extract Text بين الفقرات في مستند Word

دعونا نرى كيفية استخراج المحتوى بين فقرتين في مستند Word DOCX. فيما يلي خطوات تنفيذ هذه العملية في Java.

  • أولاً ، قم بتحميل مستند Word باستخدام فئة المستند.
  • احصل على مرجع لفقرات البداية والنهاية في كائنين باستخدام طريقة Document.getFirstSection().getChild (NodeType.PARAGRAPH، int، bool).
  • استدعاء طريقة extractContent (startPara ، endPara ، true) لاستخراج العقد في كائن.
  • استدعاء طريقة مساعدة إنشاء مستند (مستند ، عقد مستخرج) لإنشاء مستند يتكون من المحتوى المستخرج.
  • أخيرًا ، احفظ المستند المرتجع باستخدام طريقة Document.save (String).

يوضح نموذج التعليمات البرمجية التالي كيفية استخراج النص بين الفقرتين السابعة والحادية عشرة في Word DOCX في Java.

// تحميل المستند
Document doc = new Document("TestFile.doc");

// اجمع العقد. يستخدم أسلوب GetChild فهرسًا يستند إلى 0
Paragraph startPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 6, true);
Paragraph endPara = (Paragraph) doc.getFirstSection().getChild(NodeType.PARAGRAPH, 10, true);
// استخرج المحتوى بين هذه العقد في المستند. قم بتضمين هذه
// علامات في الاستخراج.
ArrayList extractedNodes = extractContent(startPara, endPara, true);

// أدخل المحتوى في مستند منفصل جديد واحفظه على القرص.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

جافا استخراج النص من DOC - بين أنواع مختلفة من العقد

يمكنك أيضًا استخراج المحتوى بين أنواع العقد المختلفة. للتوضيح ، دعنا نستخرج المحتوى بين فقرة وجدول ونحفظه في مستند Word جديد. فيما يلي خطوات استخراج النص بين العقد المختلفة في مستند Word في Java.

  • قم بتحميل مستند Word باستخدام فئة المستند.
  • احصل على مرجع لعقدتي البداية والنهاية في كائنين باستخدام طريقة Document.getFirstSection().getChild (NodeType، int، bool).
  • استدعاء طريقة extractContent (startPara ، endPara ، true) لاستخراج العقد في كائن.
  • استدعاء طريقة مساعدة إنشاء مستند (مستند ، عقد مستخرج) لإنشاء مستند يتكون من المحتوى المستخرج.
  • احفظ المستند المرتجع باستخدام طريقة Document.save (String).

يوضح نموذج التعليمات البرمجية التالي كيفية استخراج نص بين فقرة وجدول في DOCX باستخدام Java.

// تحميل المستندات
Document doc = new Document("TestFile.doc");

// احصل على مرجع لفقرة البداية
Paragraph startPara = (Paragraph) doc.getLastSection().getChild(NodeType.PARAGRAPH, 2, true);
Table endTable = (Table) doc.getLastSection().getChild(NodeType.TABLE, 0, true);

// استخرج المحتوى بين هذه العقد في المستند. قم بتضمين هذه العلامات في الاستخراج.
ArrayList extractedNodes = extractContent(startPara, endTable, true);

// يتيح عكس المصفوفة لتسهيل عملية إدراج المحتوى مرة أخرى في المستند.
Collections.reverse(extractedNodes);

while (extractedNodes.size() > 0) {
    // أدخل العقدة الأخيرة من القائمة المعكوسة
    endTable.getParentNode().insertAfter((Node) extractedNodes.get(0), endTable);
    // قم بإزالة هذه العقدة من القائمة بعد الإدراج.
    extractedNodes.remove(0);
}

// احفظ المستند الذي تم إنشاؤه على القرص.
doc.save("output.doc");

Java Extracting Text from DOCX - بين الفقرات بناءً على الأنماط

دعنا الآن نتحقق من كيفية استخراج المحتوى بين الفقرات بناءً على الأنماط. للتوضيح ، سنقوم باستخراج المحتوى بين “العنوان 1” الأول و “العنوان 3” الأول في مستند Word. توضح الخطوات التالية كيفية تحقيق ذلك في Java.

  • أولاً ، قم بتحميل مستند Word باستخدام فئة المستند.
  • بعد ذلك ، قم باستخراج الفقرات إلى كائن باستخدام أسلوب مساعد paragraphByStyleName (المستند ، “العنوان 1”).
  • استخراج فقرات إلى كائن آخر باستخدام أسلوب مساعد paragraphByStyleName (المستند ، “العنوان 3”).
  • استدعاء طريقة extractContent (startPara ، endPara ، true) وتمرير العناصر الأولى في كل من مصفوفات الفقرة كمعامل أول وثاني.
  • استدعاء طريقة مساعدة إنشاء مستند (مستند ، عقد مستخرج) لإنشاء مستند يتكون من المحتوى المستخرج.
  • أخيرًا ، احفظ المستند المرتجع باستخدام طريقة Document.save (String).

يوضح نموذج التعليمات البرمجية التالي كيفية استخراج المحتوى بين الفقرات بناءً على الأنماط.

// تحميل المستند
Document doc = new Document(dataDir + "TestFile.doc");

// قم بتجميع قائمة بالفقرات باستخدام أنماط العناوين الخاصة بها.
ArrayList parasStyleHeading1 = paragraphsByStyleName(doc, "Heading 1");
ArrayList parasStyleHeading3 = paragraphsByStyleName(doc, "Heading 3");

// استخدم المثيل الأول للفقرات بهذه الأنماط.
Node startPara1 = (Node) parasStyleHeading1.get(0);
Node endPara1 = (Node) parasStyleHeading3.get(0);

// استخرج المحتوى بين هذه العقد في المستند. لا تقم بتضمين هذه العلامات في الاستخراج.
ArrayList extractedNodes = extractContent(startPara1, endPara1, false);

// أدخل المحتوى في مستند منفصل جديد واحفظه على القرص.
Document dstDoc = generateDocument(doc, extractedNodes);
dstDoc.save("output.doc");

Java Word Text Extractor - قراءة المزيد

يمكنك استكشاف سيناريوهات أخرى لاستخراج النص من مستندات Word باستخدام مقالة التوثيق هذه.

Java API لاستخراج النص من DOC / DOCX - احصل على ترخيص مجاني

يمكنك الحصول على ترخيص مؤقت لاستخدام Aspose.Words لجافا بدون قيود تقييم.

استنتاج

في هذه المقالة ، تعلمت كيفية استخراج نص من MS Word DOC DOCX في Java. علاوة على ذلك ، لقد رأيت كيفية استخراج المحتوى بين أنواع متشابهة أو مختلفة من العقد في مستند Word برمجيًا. وبالتالي ، يمكنك بناء مستخرج نصوص MS Word الخاص بك في Java. بالإضافة إلى ذلك ، يمكنك استكشاف ميزات أخرى لـ Aspose.Words لجافا باستخدام التوثيق. في حال كان لديك أي أسئلة ، لا تتردد في إخبارنا عبر المنتدى.

أنظر أيضا