ממיר רישיות

המרת טקסט בין camelCase, snake_case, kebab-case, Title Case ועוד, עם מיפוי רישיות תלוי-שפה וכלי שורות. הכול בדפדפן שלכם.

טקסט

צורות כתיב

  • UPPERCASE
    XMLHTTPREQUEST HANDLER FOR ISTANBUL CITY GUIDE
  • lowercase
    xmlhttprequest handler for istanbul city guide
  • Title Case
    Xmlhttprequest Handler For Istanbul City Guide
  • Sentence case
    Xmlhttprequest handler for istanbul city guide
  • camelCase
    xmlHttpRequestHandlerForIstanbulCityGuide
  • PascalCase
    XmlHttpRequestHandlerForIstanbulCityGuide
  • snake_case
    xml_http_request_handler_for_istanbul_city_guide
  • kebab-case
    xml-http-request-handler-for-istanbul-city-guide
  • CONSTANT_CASE
    XML_HTTP_REQUEST_HANDLER_FOR_ISTANBUL_CITY_GUIDE
  • dot.case
    xml.http.request.handler.for.istanbul.city.guide

פעולות שורות

תוצאה
XMLHttpRequest handler for istanbul city guide

שורות: 1

פיצול מילים אחד, עשר צורות כתיב

שינוי רישיות של טקסט נראה כמו הדבר הפשוט ביותר שתוכנה יכולה לעשות, והוא מקור למספר מפתיע של באגים אמיתיים. חלק מהסיבה הוא ש"רישיות" הן למעשה שתי בעיות נפרדות שחולקות שם אחד. המרה ל-UPPERCASE או ל-lowercase היא מיפוי תו-אחר-תו שמוגדר ב-Unicode. המרה ל-camelCase או ל-snake_case היא משהו אחר לגמרי: קודם צריך להחליט היכן נמצאות המילים, ורק אז להמיר רישיות ולחבר מחדש. הכלי הזה עושה את שניהם, מציג את כל התוצאות בבת אחת, ובעיקר — אומר לכם אילו מהתשובות תלויות בהגדרת שפה.

שש צורות המתכנת נגזרות כולן מאותו פיצול מילים, ולכן הן לעולם לא יכולות לסתור זו את זו. אלו הגבולות שלו:

  • אות קטנה שאחריה גדולה מתחילה מילה חדשה, וזה מה שהופך את fooBar ל-foo ול-Bar.
  • רצף אותיות גדולות מסתיים במקום שבו מתחילה המילה הבאה — כלומר באות הגדולה האחרונה שלפני אות קטנה. XMLHttpRequest הופך ל-XML, Http ו-Request; IOError הופך ל-IO ו-Error.
  • ספרה שאחריה אות גדולה מתחילה מילה חדשה, ולכן html5Parser הופך ל-html5 ול-Parser.
  • כל מה שאינו אות, ספרה או סימן משולב מפריד בין מילים: רווחים, קווים תחתונים, מקפים, נקודות וסימני פיסוק.

מלבד זאת ספרות נשארות מחוברות למילה שלפניהן, ולכן utf8 ו-address2 עוברים בשלמותם. זו סטייה מכוונת מ-lodash, שהמפצל שלו מתייחס לכל רצף ספרות כמילה בפני עצמה והופך את utf8 ל-utf_8 — עקבי מבחינה טכנית, אבל לא מה שמישהו שנתן שם למשתנה התכוון אליו.

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

ראשי תיבות: שתי תשובות נכונות

אחרי ש-XMLHttpRequest פוצל ל-XML, Http ו-Request, ההרכבה מחדש כ-PascalCase מעלה שאלה שאין לה תשובה נכונה יחידה. אם ממירים רישיות בכל מילה מקבלים XmlHttpRequest, וזה מה ש-lodash מפיק ומה שמדריך הסגנון של Google ל-Java דורש: להתייחס לראשי תיבות כמילה רגילה כדי שהגבולות יישארו גלויים בשמות כמו ParseXmlDocument. אם משאירים את רצף האותיות הגדולות מקבלים XMLHttpRequest, וזה השם האמיתי של ה-API בדפדפן והמוסכמה שרוב ה-DOM וחלק גדול מ-‎.NET הולכים לפיה.

שתי הצורות בשימוש בבסיסי קוד אמיתיים, ולכן הכלי מציע את שתיהן וברירת המחדל היא המרת הרישיות. האפשרות משפיעה רק על camelCase ועל PascalCase — שאר הצורות ממירות ממילא את המילה כולה, ולכן XMLHttpRequest הוא xml_http_request ו-XML_HTTP_REQUEST בכל מקרה.

פרט אחד קבוע ולא ניתן לבחירה: המילה הראשונה בשם camelCase תמיד עוברת המרה מלאה לאותיות קטנות, גם כשהיא ראשי תיבות. שמירה עליה הייתה מייצרת PascalCase, ושמירה חלקית הייתה מייצרת xMLHttpRequest — צורה שאף מדריך סגנון אינו ממליץ עליה וכל קורא נתקל בה. לכן XMLHttpRequest הופך ל-xmlHttpRequest בשני המצבים, בעוד parseXMLDocument שומר על ראשי התיבות שבאמצעו כשמבקשים זאת.

ה-i הטורקית, ולמה השפה היא בחירה

ל-JavaScript שתי שיטות להמרה לאותיות גדולות, וההבדל ביניהן הפיל מערכות בייצור. toUpperCase מפעילה את מיפוי Unicode שאינו תלוי בשפה: האות i הופכת ל-I. toLocaleUpperCase מפעילה את הכללים של שפה מסוימת, ובתורכית ובאזרית האות הגדולה של i היא İ עם נקודה, משום שבאלפביתים האלה קיימת גם ı בלי נקודה שהאות הגדולה שלה היא I. המירו קלט של משתמש תורכי לאותיות גדולות לפי השפה שלו, השוו אותו מול מילת מפתח של פרוטוקול, וההשוואה תיכשל על תו שנראה כמעט זהה.

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

'i'.toUpperCase()             // 'I'
'i'.toLocaleUpperCase('tr')   // 'İ'  dotted capital
'I'.toLocaleLowerCase('tr')   // 'ı'  dotless lowercase
'ISTANBUL'.toLocaleLowerCase('tr')  // 'ıstanbul'

שלוש שפות נוספות משנות את התשובה, והכלי מציע את כולן. ליטאית שומרת נקודה מעל ה-i בהמרה לאות קטנה של אות גדולה שנושאת סימן נוסף, ולכן Ì הופכת ל-i ואחריה נקודה משולבת וגרש משולב, ולא לתו המורכב היחיד ì. יוונית משמיטה את הטונוס בהמרה לאותיות גדולות, ולכן άνθρωπος הופך ל-ΑΝΘΡΩΠΟΣ ולא ל-ΆΝΘΡΩΠΟΣ, משום שביוונית אין כותבים את הטעם על אותיות גדולות. אזרית חולקת עם התורכית את ה-i עם הנקודה ובלעדיה.

שני מקרים מפורסמים אינם כללי שפה, וכדאי לדעת מי מהם מי. ה-ß הגרמנית הופכת ל-SS בכל שפה, משום שזהו כלל SpecialCasing של Unicode ולא כלל גרמני — וזו גם הסיבה שהמיפוי אינו הפיך, ו-SS חוזר ל-ss. וסיגמה יוונית בסוף מילה הופכת לצורה הסופית ς ולא ל-σ, בכל מקום: זהו תנאי הקשרי על מיקום התו ולא הגדרת שפה. שניהם נראים בכלי הזה גם בלי לבחור שפה כלל.

Title case ו-Sentence case כאן הם מכניים

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

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

תנאי הרווח ב-Sentence case הוא מה שמונע מ-‎3.5 metres להפוך ל-‎3.5 Metres, משום שהנקודה שם באה לפני ספרה. הוא אינו פותר קיצורים: e.g. this עדיין מתחיל משפט מדומה, כי הידיעה ש-"e.g." אינו סוף משפט דורשת רשימה של הקיצורים באותה שפה. גם גרש בתוך מילה אינו מתחיל מילה חדשה, ולכן it's מקבל אות גדולה פעם אחת ונותן It's ולא It'S.

מיון: סדר אלפביתי או סדר בייטים

כלי השורות מקצצים רווחים, מסירים כפילויות, ממיינים, הופכים סדר וממספרים שורות, והמיון מעלה את שאלת השפה בפעם השנייה. סדר אלפביתי הוא תכונה של שפה ולא של Unicode. גרמנית משבצת את ä ליד a, כלומר a, ä, z. שוודית מתייחסת אליה כאות נפרדת בסוף האלפבית, כלומר a, z, ä. שתיהן נכונות במדינה שלהן, והכלי משתמש בשפה שבחרתם בראש העמוד.

האפשרות השנייה היא סדר לפי נקודת קוד, וזה מה שרוצים כשהתשובה חייבת להתאים למשהו אחר. השוואה לפי נקודת קוד היא סדר הבייטים ב-UTF-8, ולכן היא משחזרת בדיוק את מה ש-LC_ALL=C sort, git ורוב ההשוואות המובנות בשפות תכנות מפיקים — כל האותיות הגדולות לפני כל הקטנות, כך ש-B קודם ל-a. בחרו בה כשאתם משווים מול פלט של כלי; בחרו ב-collation כשאדם עומד לקרוא את הרשימה.

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

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

היכן זה רץ

הכול קורה בדפדפן שלכם. מיפויי הרישיות מגיעים מהמימוש של Unicode במנוע ה-JavaScript עצמו וה-collation מ-Intl.Collator, ולכן התשובות מסכימות עם סביבת הריצה שתעבד בפועל את המחרוזות שלכם ולא עם טבלה שמישהו העתיק לפני שנים. שום דבר שתדביקו אינו מועלה, נשמר או נרשם, ולכן הכלי בטוח לשימוש על שמות, שורות לוג וכל דבר אחר שהייתם מעדיפים לא לשלוח לשרת רק כדי לשנות את הרישיות שלו.

שאלות נפוצות

האם הטקסט שלי נשלח לשרת?
לא. כל המרה רצה בדפדפן שלכם באמצעות מיפויי הרישיות של Unicode במנוע ה-JavaScript עצמו, ושום דבר שתדביקו אינו מועלה או נרשם.
למה בחירת תורכית מציגה לי שתי תוצאות שונות?
משום שתורכית משנה את התשובה. באלפבית שלה יש גם i עם נקודה וגם ı בלעדיה, ולכן האות הגדולה של i היא İ ולא I. בכל פעם ששפה משנה תוצאה הכלי מציג לצידה את התשובה שאינה תלויה בשפה, כי זו הנחוצה למזהים ולאסימוני פרוטוקול.
האם להשתמש במיפוי שאינו תלוי בשפה או במיפוי של שפה?
השתמשו במיפוי שאינו תלוי בשפה לכל מה שמכונה תקרא: מזהים, כותרות HTTP, מפתחות במסד נתונים, אסימוני פרוטוקול ושמות קבצים. השתמשו במיפוי של שפה לטקסט שאדם יקרא באותה שפה. הבלבול בין השניים הוא באג ה-i הטורקית הקלאסי.
למה XMLHttpRequest מומר ל-XmlHttpRequest?
משום שראשי תיבות עוברים המרת רישיות כברירת מחדל, וכך עושים lodash וכמה מדריכי סגנון מרכזיים. הפעילו את אפשרות ראשי התיבות כדי לשמור על רצף האותיות הגדולות ולקבל בחזרה XMLHttpRequest. היא משפיעה רק על camelCase ועל PascalCase, מפני ששאר הצורות ממירות ממילא כל מילה.
למה utf8 נשאר מילה אחת בעוד lodash מפצל אותו?
משום שכאן ספרות נשארות מחוברות למילה שלפניהן, ולכן utf8 ו-address2 עוברים בשלמותם. ספרה שאחריה אות גדולה כן מתחילה מילה חדשה, וזה מה שמפצל את html5Parser ל-html5 ול-Parser.
למה Title Case מגדיל מילים קצרות כמו "of" ו-"the"?
משום שהכלל מכני בכוונה. מדריכי הסגנון באנגלית חלוקים זה על זה בשאלה אילו מילים קצרות נשארות באות קטנה, והמוסכמות שלהם אינן עוברות לשאר השפות שהאתר הזה משרת. הגדלת כל מילה היא צפויה וקלה לתיקון ידני.
למה אי אפשר להשתמש במיון מספרי טבעי עם סדר נקודות קוד?
מיון טבעי הוא תכונה של collation. סדר נקודות קוד הוא סדר בייטים, שאין בו מושג של מספר בתוך מחרוזת — וגם ל-LC_ALL=C sort שהוא משחזר אין. הצעת שניהם יחד הייתה מפיקה פלט שכבר אינו תואם לכלי שהוא מחקה.
למה ß הופכת ל-SS גם בלי שנבחרה שפה?
משום שהמיפוי הזה הוא כלל SpecialCasing של Unicode ולא כלל של השפה הגרמנית, ולכן הוא חל בכל מקום. הוא גם אינו הפיך: SS הופך ל-ss ולא בחזרה ל-ß. הסיגמה הסופית ביוונית פועלת באותו אופן — היא תלויה במיקום האות ולא בשפה.