en-UShe-IL
You are here:   Blog
Register   |  Login

Blog Archive:

Maximize
* Can be used in order to search for older blogs Entries

Search in blogs


Blog Categories:

Maximize
* Can be used in order to search for blogs Entries by Categories

Blog Tags:

Maximize
* Can be used in order to search for blogs by keywords

TNWikiSummit


Awared MVP 


 


Microsoft® Community Contributor 


Microsoft® Community Contributor


 Read first, before you you use the blog! Maximize
אוג26

Written by: ronen ariely
26/08/2011 14:39 RssIcon

תו האפס בקידוד אסקי

 

רקע:

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

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

לפני תקן יוניקוד היה מקובל לעבוד עם תקן ASCII, שייצג את האלפבית הלטיני הפשוט (ללא אקצנטים וכו'), מספרים וסימני פיסוק בקידוד של 7 סיביות (2^7=128 מספרים מ 0 ועד 127).

על מנת לתמוך בתווים נוספים של שפה מקומית הורחב ייצוג ASCII לקידוד של 8 סיביות, שהכיל 256 תווים: 128 תווי ASCII המקוריים ו-128 תווים אחרים שנקבעו לפי האזור (הגדרת ה Culture בו עובדים). כך למשל, המספר 224 הכיל את האות à בקידוד של מערב אירופה ואת האות א בקידוד העברי.

מדריך זה ידון בתו הראשון שיש לנו בקידוד ASCII, תו אפס או בשמו Null character (נקרא גם null terminator ואנחנו עוד נדון במשמעות השם).

הבעיה והמורכבות הקשורים לתו האפס

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

using C or C++

שרשראות הם למעשה אוסף של תווים. אנו יכולים להגדיר אותם כמערך פשוט של אלמנט CHAR. כאשר אנו עושים שימוש השרשרת "hello" נוסף לנו תו אפס באופן אוטומטי בעוד כאשר אנו עובדים במפורש עם אוסף של תווים כגון { 'H', 'e', 'l', 'l', 'o', '\0' } אנחנו חייבים לזכור להוסיף בסיום האוסף את תו האפס. אם לא נוסיף את תו האפס בסיום התנהגויות מוזרות יכולות להתקבל בתוצאה בזמן הצגה של האוסף כגון הצגה של התווים השמורים בזיכרון עד למיקום הבא של תו האפס '\0'

* הערה: הסימון '\0\ זהה לשימוש ב char(0)

Example : this code lines are the same

char myword [] = { 'H', 'e', 'l', 'l', 'o', '\0' };

char myword [] = "Hello";

Example:

cout << "hello\0world";

Result:

hello

Explain:

בתוצאה קיבלנו רק את החלק השרשרת המופיע לפני תו האפס "hello". ברגע שההמערכת הגיע לתו האפס מבחינתה השרשרת הסתיימה.

Example:

char world[] = {'W','o','r','l','d'};

char hello[]={'H','e','l','l','o','\0'};

cout << world;

Result (this e Result is OS- and compiler-dependant!):

WorldÌÌÌÌÌÌÌÌÌÌÌHello

 Explain:

המשתנה hello הוכנס לזיכרון כמה בייט אחרי הכנסת המשתנה world לזיכרון. כאשר אנו מנסים לקרוא את המשתנה הראשון ולקבל רק אותו הרי שבאופן מעשי קיבלנו גם את התווים השמורים במשתנה הראשון וגם את אלו מהמשתנה השני (וכן את המקומות הריקים בזיכרון שנשמרו עבור תווים נוספים). הדבר קרה היות ולא היה תו אפס בין התחלת הקריאה לשרשרת ועד לסיום קריאת התווים של המשתנה השני.

Using SQL

Example:

declare @Str as nvarchar(100) =  (CHAR(48) + CHAR(49) + CHAR(50) + CHAR(51) + CHAR(0) + CHAR(55) + CHAR(56) + CHAR(57) + CHAR(58))

select @Str,LEN(@Str)

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

Example:

declare @MyStr as nvarchar(100)

set @MyStr = 'Start String:' + char(0) + 'End String'

select @MyStr

select REPLACE(@MyStr,char(0),'')

select LEN(@MyStr)

* דוגמה זו תמש אותנו בהמשך להציג פתרון לבעית עבודה עם תו אפס בשרת SQL

Using C sharp

Using c sharp we can use Null character directly, and the compiler should take it as a regular part of an array of characters. As it cannot print it on CONSOLE we shell get blank space.

StringBuilder StrBuilder = new StringBuilder(100);

for (int x = 48; x < 58; x++)

{

    // we add Null character before the char 51

    // that is actually the number 2.

    if (x == 51) { StrBuilder.Append((char)0); }

    StrBuilder.Append((char)x);

}

Console.WriteLine(StrBuilder.ToString());

Console.ReadLine();

 

הפתרון והדרך לעקוף בעיות

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

נקודה נוספת שנזכור להמשך העבודה היא שהגדרת התווים נעשית בהגדרת השימוש בקידוד. כפי שהסברנו בתחילת המדריך הרי שהגדרה זו מושפעת באזור  שלנו (הגדרת ה Culture בו עובדים). אני בטוח שעד לרגע זה חשבתם שההקדמה מיותרת :-) בעוד ההקדמה וההבנה ההיסטוריה היא הבסיס לפתרון חלק ניכר מהבעיות.

עבודה בשרת SQL:

פתרון 1: עבודה עם טקסטים בשפה האנגלית

בשרת SQL ניתן למצוא שהוגדרו עברנו מספר רב של הגדרות אזורים. רשימה מלאה של האזורים השונים ניתן למצוא בקישור הבא: http://msdn.microsoft.com/en-us/library/ms143508.aspx

המעבר לשימוש בפורמט של אזור אחד או אחר נעשית באמצעות ההוראה COLLATE. הגדרת אזור בנויה ממספר מאפיינים המופרדים בינן בקו תחתון (קישור למדריך בנושא  CULTURE אני אפנה כאן בהמשך כשאכתוב אחד אם אזוכר...בינתיים בקצרה...). כך למשל אם נסתכל על ה collation העברי Hebrew_CI_AS הרי שנוכל לראות שהוא מורכב משלושה חלקים:

  • Hebrew : מאפיין הקובע את השפה
  • CI             : case-sensitive, מאפיין הקובע רגישות לאותיות גדולות
  • AS           : accent-sensitive, קובע את אופן מיון הנתונים

אם נעבור במהירות על כל האפשרויות שיש לנו בקישור הנ"ל אז נוכל לראות collation אחד השונה מכל האחרים ומתחיל בסימון SQL לפני הגדרת השפה: SQL_Latin1_General_CP1_CI_AS.

אני לא יודע אם זה מדוייק אבל אישית אני נוהג לקרוא ל collation זה בשם what you see is what you get :-). Collation זה מתייחס לנתונים כפי שהם. כך למשל שרשרת תווים תתייחס כאוסף תווים בלי קשר לערך התווים. לצערנו קיים לנו collation כזה רק עבור השפה האנגלית ולכן עבור השפה האנדלית יש לנו פתרון מיידי ומהיר לבעיה שלנו:

declare @MyStr as nvarchar(100)

set @MyStr = 'Start String:' + char(97) + char(0) + char(98) + 'End String'

 

-- will not work

select @MyStr

 

-- will not work

select REPLACE(@MyStr,char(0),'')

 

-- Work !

select replace(

      cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max))

      , char(0)

      , ''

);

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

פתרון 2: עבודה עם טקסטים בעברית (פתרון לא יעיל מבחינת משאבים)

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

שלב א:

אפשרות A: נמצא את המיקום של כל תווי האפס שלנו על ידי ביצוע חיפוש בטקסט לאחר שהפכנו אותו לאזור SQL_Latin1_General_CP1_CI_AS

אפשרות B: נמצא את המיקום של תוו האפס באמצעות הפונקציה CHARINDEX

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

declare @MyStr as nvarchar(100)

set @MyStr =

      'התחלה:'

      + char(0)

      + 'סיום.'

 

-- will not work

select @MyStr

 

-- will not work

select REPLACE(@MyStr,char(0),'')

 

-- Work for english only!

select replace(

      cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max))

      , char(0)

      , ''

);

 

-- Example of using CHARINDEX

select CHARINDEX( CHAR(0), cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max)) )

-- Final Solution

select

            LEFT(@MyStr

                        ,CHARINDEX( CHAR(0), cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max)) ) -1

                        )

            +

            SUBSTRING(@MyStr

                        ,CHARINDEX( CHAR(0), cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max)) ) + 1

                        ,100

                        )

דוגמה מלאה לניקוי כל תווי האפס שיש בשרשרת על פי שיטה זו:

declare @MyStr as nvarchar(100)

set @MyStr = 'התחלה:' + char(0) + 'אמצע' + char(0) + 'סיום.'

 

 

while CHARINDEX( CHAR(0), cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max)) ) > 0

begin

      select @MyStr =

            LEFT(@MyStr

                        ,CHARINDEX( CHAR(0), cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max)) ) -1

                        )

            +

            SUBSTRING(@MyStr

                        ,CHARINDEX( CHAR(0), cast(@MyStr COLLATE SQL_Latin1_General_CP1_CI_AS as varchar(max)) ) + 1

                        ,100

                        )

end

 

select @MyStr

פתרון 3: עבודה עם טקסט בכל שפה (יעילות מקסימאלית – עבודה עם CLR)

לא יאמן אבל שורת קוד אחת פשוט תעבוד :-)
כפי שראינו
C Sharp וכל שפות דו טנט עובדים עם שרשרת התווים תמיד במצב של אוסף תווים ולכן ניתן לבצע replace פשוט ומיידי לכל תווי האפס שלנו.

חומר עזר:

 

Tags: SQL
Categories: SQL , C# , C