חלץ טקסט ממסמכי MS Word ב-Java

במקרים שונים, ייתכן שיהיה עליך לחלץ טקסט ממסמכי Word באופן תוכנתי. לדוגמה, עבור ניתוח טקסט, חילוץ של קטעים מסוימים וכו’. במקרים כאלה, מאמר זה מספק שיטה מהירה וקלה ליישום לחילוץ טקסט ממסמכי Word ב-Java. כמו כן, זה יאפשר לך לחלץ טקסט בין חלקים/אלמנטים שונים של מסמך Word.

ספריית Java לחילוץ טקסט ממסמכי Word

Aspose.Words for Java היא ספרייה רבת עוצמה המאפשרת לך ליצור מסמכי MS Word מאפס. יתר על כן, זה מאפשר לך לתפעל מסמכי Word קיימים לצורך הצפנה, המרה, חילוץ טקסט וכו’. אנו נשתמש בספרייה זו כדי לחלץ טקסט ממסמכי Word DOCX או DOC. אתה יכול להוריד את ה-JAR של ה-API או להתקין אותו באמצעות התצורות הבאות של 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

מסמך MS Word מורכב מאלמנטים שונים, הכוללים פסקאות, טבלאות, תמונות וכו’. לכן, הדרישות לחילוץ טקסט עשויות להשתנות מתרחיש אחד למשנהו. לדוגמה, ייתכן שיהיה עליך לחלץ טקסט בין פסקאות, סימניות, הערות וכו'.

כל סוג של אלמנט ב- Word DOC/DOCX מיוצג כצומת. לכן, כדי לעבד מסמך, תצטרך לשחק עם הצמתים. אז בואו נתחיל ונראה כיצד לחלץ טקסט ממסמכי Word בתרחישים שונים.

חלץ טקסט מ- Word DOC ב-Java

בסעיף זה, אנו הולכים ליישם מחלץ טקסט Java עבור מסמכי Word וזרימת העבודה של חילוץ הטקסט תהיה כדלקמן:

  • ראשית, נגדיר את הצמתים שברצוננו לכלול בתהליך חילוץ הטקסט.
  • לאחר מכן, נחלץ את התוכן בין הצמתים שצוינו (כולל או לא כולל את צמתי ההתחלה והסיום).
  • לבסוף, נשתמש בשיבוט של צמתים שחולצו, למשל כדי ליצור מסמך Word חדש המורכב מתוכן שחולץ.

כעת נכתוב שיטה בשם extractContent אליה נעביר את הצמתים ועוד כמה פרמטרים לביצוע חילוץ הטקסט. שיטה זו תנתח את המסמך ותשכפל את הצמתים. להלן הפרמטרים שנעביר לשיטה זו.

  1. startNode ו-endNode כנקודות התחלה וסיום לחילוץ התוכן, בהתאמה. אלה יכולים להיות צמתים ברמת בלוק (סעיף, טבלה) או ברמת מוטבעת (למשל הפעלה, 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(true);

        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);

        // עבור לצומת הבא וחלץ אותו. אם הצומת הבא הוא null זה אומר ששאר התוכן נמצא בקטע אחר.
        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.

חלץ טקסט בין פסקאות של מסמך Word

בואו נראה כיצד לחלץ תוכן בין שתי פסקאות במסמך Word DOCX. להלן השלבים לביצוע פעולה זו ב-Java.

  • ראשית, טען את מסמך Word באמצעות מחלקה Document.
  • קבל הפניה של פסקאות ההתחלה והסיום לשני אובייקטים באמצעות שיטת Document.getFirstSection().getChild(NodeType.PARAGRAPH, int, bool).
  • קרא לשיטת extractContent(startPara, endPara, true) כדי לחלץ את הצמתים לאובייקט.
  • קרא לשיטת העזר generDocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • לבסוף, שמור את המסמך המוחזר באמצעות שיטת Document.save(String).

דוגמת הקוד הבאה מראה כיצד לחלץ טקסט בין הפסקה השביעית ל-11 ב- 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");

חלץ טקסט בין צמתים שונים של מסמך Word

אתה יכול גם לחלץ תוכן בין סוגים שונים של צמתים. להדגמה, בואו נחלץ תוכן בין פסקה לטבלה ונשמור אותו במסמך Word חדש. להלן השלבים לחילוץ טקסט בין צמתים שונים במסמך Word ב-Java.

  • טען את מסמך Word באמצעות מחלקה Document.
  • קבל הפניה של צמתי ההתחלה והסיום לשני אובייקטים באמצעות שיטת Document.getFirstSection().getChild(NodeType, int, bool).
  • קרא לשיטת extractContent(startPara, endPara, true) כדי לחלץ את הצמתים לאובייקט.
  • קרא לשיטת העזר generDocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • שמור את המסמך שהוחזר באמצעות שיטת Document.save(String).

דוגמת הקוד הבאה מראה כיצד לחלץ טקסט בין פסקה לטבלה ב- Word 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");

קבל טקסט בין פסקאות בהתבסס על סגנונות

כעת נבדוק כיצד לחלץ תוכן בין פסקאות על סמך סגנונות. לצורך הדגמה, אנו הולכים לחלץ תוכן בין “כותרת 1” הראשונה ל"כותרת 3" הראשונה במסמך Word. השלבים הבאים מדגימים כיצד להשיג זאת ב-Java.

  • ראשית, טען את מסמך Word באמצעות מחלקה Document.
  • לאחר מכן, חלץ פסקאות לאובייקט באמצעות שיטת העזרה paragraphsByStyleName(Document, “Heading 1”).
  • חלץ פסקאות לאובייקט אחר באמצעות שיטת העזרה paragraphsByStyleName(Document, “Heading 3”).
  • קראו לשיטת extractContent(startPara, endPara, true) והעבירו את האלמנטים הראשונים בשני מערכי הפסקה כפרמטרים ראשון ושני.
  • קרא לשיטת העזר generDocument(Document, extractedNodes) כדי ליצור מסמך המורכב מהתוכן שחולץ.
  • לבסוף, שמור את המסמך המוחזר באמצעות שיטת 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");

Extractor Word Text בחינם עבור Java

אתה יכול לקבל [רישיון זמני] בחינם6 לחילוץ טקסט ממסמכי Word ללא מגבלות הערכה.

חקור את ספריית Java DOCX

אתה יכול לחקור תרחישים אחרים של חילוץ טקסט ממסמכי Word באמצעות מאמר התיעוד זה. חוץ מזה, אתה יכול לחקור תכונות אחרות של Aspose.Words עבור Java באמצעות תיעוד. למקרה שיש לך שאלות כלשהן, אל תהסס ליידע אותנו דרך הפורום שלנו.

סיכום

במאמר זה, למדת כיצד לחלץ טקסט ממסמכי Word ב-Java. יתר על כן, ראית כיצד לחלץ תוכן בין סוגים דומים או שונים של צמתים ב-DOC או DOCX באופן תכנותי. לפיכך, אתה יכול לבנות מחלץ טקסט משלך של MS Word ב-Java.

ראה גם