אורך מחרוזת

ספירת טקסט בחמש דרכים בבת אחת: תווים כפי שסופרים אותם בעין, נקודות קוד (Python len), יחידות UTF-16 (JavaScript .length), מילים ושורות.

קלט
תווים
0
נקודות קוד
0
יחידות UTF-16
0
מילים
0
שורות
0

למה ל"כמה ארוכה המחרוזת הזו?" יש יותר מתשובה אחת

זו נשמעת שאלה טריוויאלית, אבל אורך של קטע טקסט תלוי לחלוטין במה שסופרים. אדם סופר את התווים שהוא רואה. מסד נתונים סופר את שטח האחסון שערך תופס. JavaScript סופרת משהו אחר לגמרי. כשהמספרים האלה לא מסכימים — וזה קורה הרבה עבור אמוג'י, אותיות עם ניקוד וכתבים שאינם לטיניים — "אורך" יחיד מטעה. הכלי מציג חמישה מדדים משמעותיים זה לצד זה כדי שתוכל לבחור את המתאים למצב שלך.

חמשת המדדים מוסברים

  • תווים (graphemes): מה שאדם תופס כתו אחד. אמוג'י של משפחה כמו 👨‍👩‍👧, או אות עם סימן ניקוד משולב, נספרים כאחד — זה המדד שתואם ל"בעין".
  • נקודות קוד (code points): מספר ערכי ה־Unicode. תו נראה בודד יכול להיבנות מכמה נקודות קוד מחוברות, ולכן המספר הזה לרוב גבוה יותר ממספר ה־graphemes.
  • יחידות UTF-16: הערך ש־JavaScript מדווחת מהמאפיין .length. תווים מחוץ ל־Basic Multilingual Plane (רוב האמוג'י) תופסים שתי יחידות UTF-16 כל אחד.
  • מילים: מספר האסימונים הדמויי-מילים, בזיהוי מודע ל־Unicode ולא בפיצול נאיבי לפי רווחים.
  • שורות: מספר השורות, בספירת כל מעבר שורה.

למה אמוג'י וניקוד שוברים ספירה נאיבית

טקסט מודרני בנוי מ־Unicode, שמייצג הרבה תווים נראים כרצפים ולא כיחידות בודדות. אמוג'י של דגל הוא שני סמלי אינדיקטור אזוריים. אמוג'י של משפחה הוא כמה דמויות המחוברות בתווי "מחבר ברוחב אפס" בלתי-נראים. é עם ניקוד יכולה להיות נקודת קוד אחת, או e רגילה ואחריה סימן ניקוד משולב. כל אלה נראים כתו אחד אבל יכולים להיספר כשניים, שלושה או יותר תלוי במדד. בדיוק בגלל זה ספירה ידנית, או הסתמכות על מספר אחד, מובילה לבאגים.

באיזה מדד כדאי להשתמש?

  • מגבלות תווים שהמשתמש רואה (ביו, שדה בסגנון ציוץ, תצוגת SMS): השתמש ב"תווים" (graphemes), כי זה תואם למה שהמשתמש סופר.
  • טיפול במחרוזות ב־JavaScript, אינדוקס או חיתוך: השתמש ביחידות UTF-16, כי עליהן מבוססים ‎.length ואינדקסים של מחרוזת.
  • גדלים של עמודות במסד נתונים: בדוק אם העמודה נמדדת בתווים או בבתים — רבות מוגבלות-בתים, ובתים של UTF-8 שונים שוב מכל הספירות האלה.
  • ספירת מילים או שורות לעורכים ולקריאוּת: השתמש במדדי "מילים" ו"שורות".

מה Python ומה JavaScript קוראים "אורך"

שניים מחמשת המספרים שלמעלה הם בדיוק אלה שבגללם רוב האנשים מגיעים לדף הזה, והם שייכים לשפות שונות. len()‎ של Python ו־‎.length של JavaScript מתוארים שניהם כאורך של מחרוזת, ובכל דבר שחורג מ־ASCII פשוט הם עונים תשובות שונות על אותו טקסט באותו רגע. אף אחד מהם אינו טועה: הם סופרים יחידות שונות, ושני התאים מוצגים למעלה כדי שאפשר יהיה לקרוא את הפער במקום לנחש אותו.

  • Python: len()‎ מחזירה את מספר נקודות הקוד של Unicode, כי מחרוזת ב־Python 3 היא רצף של נקודות קוד. הסתכל על התא "נקודות קוד" — זה המספר ש־len()‎ יחזיר לך.
  • JavaScript: ‎.length מחזיר את מספר יחידות הקוד של UTF-16, כי מחרוזת ב־JavaScript מוחזקת כ־UTF-16. הסתכל על התא "יחידות UTF-16". לעומת זאת Array.from(text).length עובר על המחרוזת נקודת קוד אחר נקודת קוד, ולכן מסכים עם Python.
  • אף אחת מהן אינה סופרת את מה שרואים. התא "תווים" הוא ספירת ה־graphemes, ושום אורך מובנה באף אחת מהשפות אינו מפיק אותה: JavaScript זקוקה ל־Intl.Segmenter ו־Python לחבילה חיצונית. אם המגבלה שאתה אוכף היא כזו שאדם יקרא, זה התא שאתה צריך.
  • גם "מילים" ו"שורות" אינן שייכות לאף אחת מהשפות. שתיהן יכולות לקרב אותן בפיצול לפי רווחים ולפי מעברי שורה, ושתיהן סוטות מהמספרים שכאן, שמודעים ל־Unicode — בטקסט שנכתב בלי רווחים, בסימני פיסוק, ובמעבר שורה שמסיים את הטקסט.

הדוגמה שמאחורי טעינת דוגמה היא הדרך המהירה ביותר לראות את הפער. אמוג'י המשפחה שבה הוא תו אחד עבור הקורא, חמש נקודות קוד עבור Python ושמונה יחידות UTF-16 עבור JavaScript: שלוש מחמש נקודות הקוד יושבות מחוץ ל־Basic Multilingual Plane ועולות שתי יחידות כל אחת, ושני המחברים הבלתי-נראים שביניהן עולים יחידה אחת כל אחד. כך ששדה שמותר לכתוב בו עשרים תווים אכן מכיל עשרים בעין, בעוד שבדיקה נאיבית שנכתבה באחת מהשפות דוחה טקסט שנכנס.

אורך אינו גודל בבייטים

אף אחד מחמשת המספרים שלמעלה אינו ספירת בייטים, וקל לבלבל בין השניים. ספירה היא תכונה של הטקסט עצמו; גודל בבייטים הוא תכונה של הטקסט אחרי שנכתב בקידוד כלשהו, ולכן אותה מחרוזת תופסת גודל אחד ב־UTF-8 וגודל אחר ב־UTF-16. כל מה שנמדד בבייטים נמדד לכן אחרי הקידוד, והקידוד שנבחר הוא חלק מהתשובה.

  • UTF-8 הוא מה שכמעט הכול משתמש בו, והוא ברוחב משתנה: אות, ספרה או סימן פיסוק מ־ASCII תופסים בייט אחד, רוב התווים המוטעמים והלא-לטיניים תופסים שניים או שלושה, ורבים מהאמוג'ים תופסים ארבעה. מאה תווים הם מאה בייטים רק אם כולם ASCII — אותם מאה תווים ביוונית, בעברית, בערבית או בסינית שוקלים פי שניים עד שלושה.
  • Python: len()‎ סופרת נקודות קוד, ולכן צריך לקודד לפני שמודדים. len(text.encode('utf-8'))‎ הוא מספר הבייטים ב־UTF-8, וציון קידוד אחר במקום הזה ייתן מספר אחר — וזו בדיוק הנקודה.
  • JavaScript: ‎.length סופר יחידות UTF-16, ולכן גם הוא אינו ספירת בייטים. new TextEncoder().encode(text).length הוא מספר הבייטים ב־UTF-8, גם בדפדפן וגם ב־Node, ו־TextEncoder מייצר UTF-8 בלבד, כך שאין קידוד לציין.
  • בדוק באיזו יחידה נכתבה המגבלה שלך לפני שאתה סופר משהו. יש מנועי בסיסי נתונים שמגדירים עמודה בתווים ואחרים שמגדירים אותה בבייטים, ערך של כותרת HTTP וגודל של קובץ הם בייטים, וכלל שנכתב עבור בני אדם הוא מה שהתא "תווים" מראה. כל מה שנמדד בתווים כבר מופיע למעלה; כל מה שנמדד בבייטים דורש אורך אחרי קידוד.

ברגע שיש בידך מספר בייטים, ממיר גדלי נתונים הוא המקום להמשיך בו: הוא הופך מספר בייטים גולמי לשתי מערכות היחידות בבת אחת — זו שבנויה על חזקות של אלף וזו שבנויה על חזקות של שתיים — ומראה איך כל כלי במחשב שלך מדווח על אותו גודל. הדף הזה נעצר במקום שבו מתחיל הקידוד; הדף ההוא מתחיל ממספר בייטים.

שאלות נפוצות

איזה מדד תואם ל־length של JavaScript?
הערך "יחידות UTF-16" תואם למאפיין ‎.length הגולמי ב־JavaScript. אם הקוד שלך חותך או מאנדקס מחרוזות, זה המספר שקובע את התנהגותו.
למה אמוג'י אחד נספר ככמה תווים?
הרבה אמוג'י מורכבים מכמה נקודות קוד של Unicode המחוברות יחד. כ־grapheme (מה שרואים) זהו תו אחד, אבל ספירת נקודות הקוד וה־UTF-16 גבוהה יותר, ולכן כל מדד מוצג בנפרד.
האם נספרים רווחים ומעברי שורה בסוף?
כן. כל תו שתדביק נספר, כולל רווחים בהתחלה ובסוף ומעברי שורה. אם הסכומים נראים גבוהים מהצפוי, בדוק אם יש רווחים בלתי-נראים בסוף.
האם הטקסט שלי נשלח לשרת?
לא. כל הספירה מתבצעת בדפדפן שלך. הטקסט שתקליד או תדביק לעולם אינו עוזב את המכשיר שלך.
מה זה grapheme?
grapheme, או "אשכול graphemes", הוא תו כפי שהמשתמש תופס אותו — היחידה הקטנה ביותר שקורא היה מכנה תו בודד, גם אם היא בנויה מכמה נקודות קוד.
איך בתים שונים מהספירות האלה?
בתים תלויים בקידוד. ב־UTF-8, אות ASCII היא בית אחד, רוב התווים עם ניקוד והלא-לטיניים הם שניים או שלושה, והרבה אמוג'י הם ארבעה. אף אחד מחמשת המדדים כאן אינו ספירת בתים, אז עבור שדות מוגבלי-בתים כדאי לקודד ל־UTF-8 קודם.

כלים קשורים

  • ממיר גדלי נתונים

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

  • בודק ביטויים רגולריים

    בדיקת ביטויים רגולריים — התאמה, החלפה ופיצול.

  • השוואת טקסטים

    השוואת שני טקסטים — כל שורה ומילה שהשתנו.

  • בוחן תווי יוניקוד

    לראות בדיוק מאילו תווים בנויה מחרוזת.