ניתוח וניתוח לקסיקלי
בעת כתיבת יישומי Java, אחד הדברים הנפוצים יותר שתידרש לייצר הוא מנתח. מנתחים נעים בין פשוט למורכב ומשמשים לכל דבר, החל מהתבוננות באפשרויות שורת הפקודה וכלה בפירוש קוד המקור של Java. בשנת JavaWorld בגיליון דצמבר של, הראיתי לך ג'ק, גנרטור מנתח אוטומטית כי מפרטי דקדוק ברמה גבוהה ההופכים כיתות ג 'אווה המיישמים את המנתח שתארו מפרטים אלה. החודש אראה לכם את המשאבים שג'אווה מספקת לכתיבת מנתחים ומנתחים לקסיקליים ממוקדים. המנתחים הפשוטים מעט יותר הללו ממלאים את הפער בין השוואת מחרוזות פשוטות לדקדוקים המורכבים שג'ק מחבר.
מטרת המנתחים המילוניים היא לקחת זרם של תווי קלט ולפענח אותם לאסימונים ברמה גבוהה יותר שמנתח יכול להבין. מנתחים צורכים את תפוקת המנתח הלקסיקלי ופועלים על ידי ניתוח רצף האסימונים שהוחזרו. המנתח תואם את הרצפים הללו למצב קצה, שעשוי להיות אחד ממצבי הקצה הרבים. מצבי הסיום מגדירים את היעדיםשל המנתח. כאשר מגיעים למצב קצה, התוכנית המשתמשת בניתוח עושה פעולה כלשהי - או הגדרת מבני נתונים או ביצוע קוד ספציפי לפעולה. בנוסף, מנתחים יכולים לזהות - מתוך רצף האסימונים שעובדו - כאשר לא ניתן להגיע למצב סיום חוקי; בשלב זה המנתח מזהה את המצב הנוכחי כמצב שגיאה. על היישום להחליט באיזו פעולה לנקוט כאשר המנתח מזהה מצב קצה או מצב שגיאה.
בסיס המחלקה הסטנדרטי של Java כולל כמה כיתות מנתח לקסיקליות, אולם הוא אינו מגדיר שיעורי מנתח למטרות כלליות. בטור זה אסתכל לעומק על המנתחים המילוניים המגיעים עם Java.
המנתחים המילוניים של ג'אווה
מפרט שפת Java, גרסה 1.0.2, מגדיר שתי מחלקות מנתח לקסיקליות, StringTokenizer
ו- StreamTokenizer
. משמותיהם ניתן להסיק StringTokenizer
המשתמש String
באובייקטים כקלטו StreamTokenizer
ומשתמש InputStream
באובייקטים.
כיתת StringTokenizer
מבין שני שיעורי המנתח הלקסיקלי הזמינים, הקל ביותר להבנה הוא StringTokenizer
. כאשר אתה בונה StringTokenizer
אובייקט חדש , שיטת הבנאי לוקחת באופן סמלי שני ערכים - מחרוזת קלט ומחרוזת מפרידה. לאחר מכן הכיתה בונה רצף אסימונים המייצג את התווים בין תווי התיחום.
כמנתח לקסיקלי, StringTokenizer
ניתן להגדירו רשמית כמוצג להלן.
[~ delim1, delim2, ..., delim N ] :: אסימון
הגדרה זו מורכבת מביטוי רגולרי התואם לכל תו מלבד תווי התיחום. כל התווים התואמים הסמוכים נאספים לאסימון יחיד ומחזירים אותם כאסימון.
השימוש הנפוץ ביותר StringTokenizer
בכיתה הוא להפריד קבוצה של פרמטרים - כגון רשימת מספרים המופרדת בפסיקים. StringTokenizer
הוא אידיאלי בתפקיד זה מכיוון שהוא מסיר את המפרידים ומחזיר את הנתונים. StringTokenizer
בכיתה מספק גם מנגנון לזיהוי רשימות שבהן יש "null" אסימונים. היית משתמש באסימונים null ביישומים שבהם יש פרמטרים בעלי ערכי ברירת מחדל או שאינם נדרשים להיות נוכחים בכל המקרים.
היישומון שלהלן הוא StringTokenizer
מתאמן פשוט . המקור ליישומון StringTokenizer נמצא כאן. כדי להשתמש ביישומון, הקלד טקסט כלשהו שיש לנתח באזור מחרוזת הקלט, ואז הקלד מחרוזת המורכבת מדמויות מפרידות באזור מחרוזת הפרדה. לבסוף, לחץ על ה- Tokenize! לַחְצָן. התוצאה תופיע ברשימת האסימונים מתחת למחרוזת הקלט ותארגן כאסימון אחד בכל שורה.
קחו כדוגמה מחרוזת, "a, b, d", שהועברה StringTokenizer
לאובייקט שנבנה עם פסיק (,) כתו המפריד. אם תשים ערכים אלה ביישומון המתאמן לעיל תראה Tokenizer
שהאובייקט מחזיר את המיתרים "a", "b" ו- "d". אם הכוונה שלך הייתה לציין שפרמטר אחד חסר, ייתכן שהופתעת שלא תראה שום אינדיקציה לכך ברצף האסימונים. היכולת לזהות אסימונים חסרים מופעלת על ידי הבוליאן Return Separator שניתן להגדיר בעת יצירת Tokenizer
אובייקט. כאשר פרמטר זה מוגדר בעת Tokenizer
בנייתו, מוחזר גם כל מפריד. לחץ על תיבת הסימון עבור מפריד החזרה ביישומון שלמעלה, והשאיר את המחרוזת והמפריד לבד. עכשיו הTokenizer
מחזיר "a, פסיק, ב, פסיק, פסיק וד '." על ידי ציון שאתה מקבל שתי תווי מפריד ברצף, אתה יכול לקבוע כי אסימון "null" נכלל במחרוזת הקלט.
הטריק לשימוש בהצלחה StringTokenizer
בניתוח הוא הגדרת הקלט באופן שתו המפריד לא מופיע בנתונים. ברור שאתה יכול להימנע מהגבלה זו על ידי תכנון עבור זה ביישום שלך. ניתן להשתמש בהגדרת השיטה שלהלן כחלק מיישומון המקבל צבע בצורת ערכים אדומים, ירוקים וכחולים בזרם הפרמטרים שלו.
/ ** * נתח פרמטר של הטופס "10,20,30" כ- TGB של RGB לערך צבע. * / 1 getColor צבע (שם מחרוזת) {2 נתוני מחרוזת; 3 StringTokenizer st; 4 int אדום, ירוק, כחול; 5 6 נתונים = getParameter (שם); 7 אם (נתונים == null) 8 להחזיר null; 9 10 st = StringTokenizer חדש (נתונים, ","); 11 נסה {12 אדום = Integer.parseInt (st.nextToken ()); 13 ירוק = Integer.parseInt (st.nextToken ()); 14 כחול = Integer.parseInt (st.nextToken ()); 15} לתפוס (חריג ה) {16 להחזיר אפס; // (ERROR STATE) לא הצליחה לנתח אותה 17} 18 להחזיר צבע חדש (אדום, ירוק, כחול); // (END STATE) נעשה. 19}
הקוד שלמעלה מיישם ניתוח פשוט מאוד שקורא את המחרוזת "מספר, מספר, מספר" ומחזיר Color
אובייקט חדש . בשורה 10, הקוד יוצר StringTokenizer
אובייקט חדש שמכיל את נתוני הפרמטרים (נניח ששיטה זו היא חלק מיישומון), ורשימת תווים מפרידה המורכבת מפסיקים. ואז בשורות 12, 13, 14, כל אסימון מופק מהמחרוזת ומומר למספר בשיטת Integer parseInt
. המרות אלה מוקפות try/catch
בחסימה למקרה שמחרוזות המספר לא היו מספרים תקפים או Tokenizer
שההטלה היא חריגה מכיוון שנגמרו לה האסימונים. אם כל המספרים עוברים המרה, מגיעים למצב הסיום Color
ואובייקט מוחזר; אחרת מגיעים למצב השגיאה ומחזירים null .
אחת התכונות של StringTokenizer
הכיתה היא שהוא מוערם בקלות. בדוק את השיטה getColor
שלמטה, שהיא שורות 10 עד 18 של השיטה הנ"ל.
/ ** * מנתח גוון צבע "r, g, b" Color
לאובייקט AWT . * / 1 getColor צבע (נתוני מחרוזת) {2 int אדום, ירוק, כחול; 3 StringTokenizer st = StringTokenizer חדש (נתונים, ","); 4 נסה {5 אדום = Integer.parseInt (st.nextToken ()); 6 ירוק = Integer.parseInt (st.nextToken ()); 7 כחול = Integer.parseInt (st.nextToken ()); 8} לתפוס (חריג e) {9 להחזיר null; // (שגיאת מדינה) לא הצליחה לנתח אותה 10} 11 להחזיר צבע חדש (אדום, ירוק, כחול); // (END STATE) נעשה. 12}
מנתח מעט מורכב יותר מוצג בקוד שלמטה. מנתח זה מיושם בשיטה getColors
, המוגדרת להחזרת מערך Color
אובייקטים.
/ ** * לנתח קבוצה של צבעים "r1, g1, b1: r2, g2, b2: ...: rn, gn, bn" לתוך * מערך של אובייקטים בצבע AWT. * / 1 צבע [] getColors (נתוני מחרוזת) {2 וקטור הצטברות = וקטור חדש (); 3 צבעי צבע, תוצאה []; 4 StringTokenizer st = StringTokenizer חדש (נתונים, ":"); 5 בעוד (st.hasMoreTokens ()) {6 cl = getColor (st.nextToken ()); 7 אם (cl! = Null) {8 accum.addElement (cl); 9} אחר {10 System.out.println ("שגיאה - צבע רע."); 11} 12} 13 אם (accum.size () == 0) 14 להחזיר null; 15 תוצאה = צבע חדש [accum.size ()]; 16 עבור (int i = 0; i <accum.size (); i ++) {17 result [i] = (Color) accum.elementAt (i); 18} 19 תוצאה חוזרת; 20}
בשיטה לעיל, השונה רק מעט getColor
מהשיטה, הקוד בשורות 4 עד 12 יוצר חדש Tokenizer
לחילוץ אסימונים המוקפים בתו המעי הגס (:). כפי שניתן לקרוא בתגובת התיעוד של השיטה, בשיטה זו מצפים כי גבעולי צבע יופרדו על ידי נקודתיים. כל קריאה nextToken
של StringTokenizer
הכיתה תחזור אסימון חדש עד שהחוט מוצה. האסימונים שיוחזרו יהיו מחרוזות המספרים המופרדות באמצעות פסיקים; מחרוזות האסימון הללו מוזנות getColor
, ואז מחלץ צבע משלושת המספרים. יצירת StringTokenizer
אובייקט חדש באמצעות אסימון שהוחזר על ידי StringTokenizer
אובייקט אחר מאפשרת לקוד המנתח שכתבנו להיות קצת יותר מתוחכם לגבי האופן בו הוא מפרש את קלט המחרוזת.
עד כמה שזה שימושי, בסופו של דבר תמצו את יכולות StringTokenizer
הכיתה ותצטרכו לעבור לאחיו הגדול StreamTokenizer
.
כיתת StreamTokenizer
כפי שמציע שם המחלקה, StreamTokenizer
אובייקט מצפה שהקלט שלו יגיע InputStream
ממחלקה. כמו StringTokenizer
האמור לעיל, מחלקה זו ממירה את זרם הקלט לנתחים שקוד הניתוח שלך יכול לפרש, אך שם מסתיים הדמיון.
StreamTokenizer
הוא מנתח לקסיקלי מונחה טבלה . המשמעות היא שלכל תו קלט אפשרי מוקצה משמעות, והסורק משתמש במשמעות של הדמות הנוכחית כדי להחליט מה לעשות. ביישום מחלקה זו, תווים מוקצים לאחת משלוש קטגוריות. אלו הם:
תווים של חלל לבן - המשמעות הלקסיקלית שלהם מוגבלת להפרדה בין מילים
תווי מילים - עליהם להיות מצטברים כאשר הם צמודים לדמות מילה אחרת
- תווים רגילים - יש להחזיר אותם מיד למנתח
תארו לעצמכם את יישום בכיתה זו כמכונה המדינה פשוט כי יש שתי מדינות - סרק ו לצבור . בכל מצב הקלט הוא דמות מאחת הקטגוריות לעיל. הכיתה קוראת את הדמות, בודקת את הקטגוריה שלה ועושה פעולה כלשהי ועוברת למצב הבא. הטבלה הבאה מציגה מכונת מצב זו.
מדינה | קֶלֶט | פעולה | מדינה חדשה |
---|---|---|---|
לְהִתְבַּטֵל | דמות מילה | לדחוף את הדמות לאחור | לִצְבּוֹר |
אופי רגיל | דמות החזרה | לְהִתְבַּטֵל | |
אופי חלל לבן | לצרוך אופי | לְהִתְבַּטֵל | |
לִצְבּוֹר | דמות מילה | הוסף למילה הנוכחית | לִצְבּוֹר |
אופי רגיל | להחזיר את המילה הנוכחית לדחוף את הדמות לאחור |
לְהִתְבַּטֵל | |
אופי חלל לבן | להחזיר את המילה הנוכחית לצרוך אופי |
לְהִתְבַּטֵל |
נוסף על המנגנון הפשוט הזה StreamTokenizer
הכיתה מוסיפה כמה יוריסטיות. אלה כוללים עיבוד מספרים, עיבוד מחרוזת מצוטט, עיבוד תגובות ועיבוד סוף השורה.
The first example is number processing. Certain character sequences can be interpreted as representing a numerical value. For example, the sequence of characters 1, 0, 0, ., and 0 adjacent to each other in the input stream represent the numerical value 100.0. When all of the digit characters (0 through 9), the dot character (.), and the minus (-) character are specified as being part of the word set, the StreamTokenizer
class can be told to interpret the word it is about to return as a possible number. Setting this mode is achieved by calling the parseNumbers
method on the tokenizer object that you instantiated (this is the default). If the analyzer is in the accumulate state, and the next character would not be part of a number, the currently accumulated word is checked to see if it is a valid number. If it is valid, it is returned, and the scanner moves to the next appropriate state.
הדוגמה הבאה היא עיבוד מחרוזת מצוטט. לרוב רצוי להעביר מחרוזת המוקפת בתו ציטוט (בדרך כלל כפול (") או ציטוט בודד (')) כאסימון יחיד. StreamTokenizer
המחלקה מאפשרת לך לציין כל תו כטבע ציטוט. כברירת מחדל הם הם הציטוט היחיד (') והציטוט הכפול ("). מכונת המדינה שונה כדי לצרוך תווים במצב הצבירה עד לעיבוד של תו ציטוט אחר או תו סוף שורה. כדי לאפשר לך לצטט את דמות הציטוט, המנתח מתייחס לתו הציטוט שקודם קו נטוי אחורי (\) בזרם הקלט ובתוך ציטוט כדמות מילה.