From ec9c48295563b829c747989825ed605aa50a44ec Mon Sep 17 00:00:00 2001 From: Junrui Yao <129917657+yaojunrui@users.noreply.github.com> Date: Fri, 11 Oct 2024 02:22:18 +0000 Subject: [PATCH] initial commit --- DESIGN.md | 7 + main | Bin 0 -> 17248 bytes main.c | 313 +++++++++++++++++++++++++++++++++++++++ tests/my_test.txt | 1 + tests/my_test.txt.expect | 9 ++ utfanalyzer | Bin 0 -> 17248 bytes 6 files changed, 330 insertions(+) create mode 100644 DESIGN.md create mode 100755 main create mode 100644 main.c create mode 100644 tests/my_test.txt create mode 100644 tests/my_test.txt.expect create mode 100755 utfanalyzer diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..d2e2b69 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,7 @@ +Another encoding of Unicode is UTF-32, which encodes all Unicode code points in 4 bytes. For things like ASCII, the leading 3 bytes are all 0's. What are some tradeoffs between UTF-32 and UTF-8? + +UTF-8 uses less bytes to represent the same thing with UTF-32. + +UTF-8 has a leading 10 on all the bytes past the first for multi-byte code points. This seems wasteful – if the encoding for 3 bytes were instead 1110XXXX XXXXXXXX XXXXXXXX (where X can be any bit), that would fit 20 bits, which is over a million code points worth of space, removing the need for a 4-byte encoding. What are some tradeoffs or reasons the leading 10 might be useful? Can you think of anything that could go wrong with some programs if the encoding didn't include this restriction on multi-byte code points? + +In UTF-8 , if the beginning of the byte is 10, then it means this byte is a continuation byte. This can help people identify the continuation byte and beginning byte. If we don't have the 10 at the beginning of the continuation byte, the computer may didn't read the entire code point correctly. \ No newline at end of file diff --git a/main b/main new file mode 100755 index 0000000000000000000000000000000000000000..62f6be80c777df3ef0e2e480b9bc0d6e7ad52f23 GIT binary patch literal 17248 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 PAH7patZ*r~isHWjcL#i6 literal 0 HcmV?d00001 diff --git a/main.c b/main.c new file mode 100644 index 0000000..fd06334 --- /dev/null +++ b/main.c @@ -0,0 +1,313 @@ +#include +#include +#include + +int32_t is_ascii(char str[]) +{ + for (int i = 0; str[i] != 0; i++) + { + if ((unsigned char)str[i] > 0b01111111) + { + return 0; + } + } + + return 1; +} + +int32_t capitalize_ascii(char str[]) +{ + int changed_str_num = 0; + for (int i = 0; str[i] != 0; i++) + { + if (str[i] >= 'a' && str[i] <= 'z') + { + str[i] = str[i] - 32; + changed_str_num = changed_str_num + 1; + } + } + + return changed_str_num; +} + +int32_t width_from_start_byte(char start_byte) +{ + unsigned char byte = (unsigned char)start_byte; + + if ((byte & 0b10000000) == 0) { + return 1; // ASCII + } + else if ((byte & 0b11100000) == 0b11000000) { + return 2; // 2 byte + } + else if ((byte & 0b11110000) == 0b11100000) { + return 3; // 3 byte + } + else if ((byte & 0b11111000) == 0b11110000) { + return 4; // 4 byte + } + return -1; +} + +int32_t utf8_strlen(char str[]) +{ + int counter = 0; + + for (int i = 0; str[i] != 0;) + { + // not a coutinuation byte + int width = width_from_start_byte(str[i]); + if (width == -1) + { + return -1; + + } + counter = counter + 1; + i = i+ width; + + } + + return counter; +} + +int32_t codepoint_index_to_byte_index(char str[], int32_t cpi) +{ + int cp_index = 0; + int byte_index = 0; + + while (str[byte_index] != '\0') + { + int width = width_from_start_byte(str[byte_index]); + + if (width == -1) + { + return -1; + } + + else if (cp_index == cpi) + { + return byte_index; + } + + byte_index = width + byte_index; + cp_index = cp_index + 1; + } + + return -1; +} + +void utf8_substring(char str[], int32_t cpi_start, int32_t cpi_end, char result[]) +{ + int result_index = 0; + int cp_index = 0; + + if (cpi_start >= cpi_end || cpi_end == 0 ) + { + result[0] = '\0'; + return; + } + + else + { + int byte_start = codepoint_index_to_byte_index(str, cpi_start); + int byte_end = codepoint_index_to_byte_index(str, cpi_end); + printf("%d, %d", byte_start, byte_end); + for (int i = byte_start; i < byte_end; i++) + { + result[result_index] = str[i]; + result_index = result_index + 1; + } + } + + result[result_index] = '\0'; +} + + +int32_t codepoint_at(char str[], int32_t cpi) +{ + + + int byte_index = codepoint_index_to_byte_index(str, cpi); + + if (byte_index == -1) { + return -1; + } + + + unsigned char byte = (unsigned char)str[byte_index]; + + + int width = width_from_start_byte(str[byte_index]); + + if (width == -1) { + return -1; + } + + // Decode the UTF-8 codepoint based on its width (1 to 4 bytes) + int codepoint = 0; + + if (width == 1) { + // 1-byte (ASCII) character + codepoint = byte; + } + else if (width == 2) { + // 2-byte + codepoint = ((byte & 0b00011111) * 64 + (str[byte_index + 1] & 0b00111111)); + } + else if (width == 3) { + // 3-byte + codepoint = ((byte & 0b00001111) * 4096 + + (str[byte_index + 1] & 0b00111111) * 64 + + (str[byte_index + 2] & 0b00111111)); + } + else if (width == 4) { + // 4-byte + codepoint = ((byte & 0b00000111) * 262144 + + (str[byte_index + 1] & 0b00111111) * 4096 + + (str[byte_index + 2] & 0b00111111) * 64 + + (str[byte_index + 3] & 0b00111111)); + } + + return codepoint; + +} + +// question +// Get help from the tutor +char is_animal_emoji_at(char str[], int32_t cpi) +{ + int num = codepoint_at(str, cpi); + + if ((num >= 0x1F400 && num <= 0x1F43F) + || (num >= 0x1F980 && num <= 0x1F9AE)) + { + + return 1; + } + + return 0; +} + +int main() +{ + + char buffer[50]; + printf("Enter a UTF-8 encoded string: "); + fgets(buffer, 50, stdin); + + //valid ascii + int ascii_valid = is_ascii(buffer); + printf("Valid ASCII: %s \n", ascii_valid ? "true" : "false"); + + + //uppercase ascii + //can't use strcpy because the buffer is not all ascii + char copy_buffer[50]; + for (int i = 0; buffer[i] != 0; i++){ + if (buffer[i] >= 'a' && buffer[i] <= 'z'){ + copy_buffer[i] = buffer[i] - 32; + } + + else{ + copy_buffer[i] = buffer[i]; + } + + } + + printf("Uppercased ASCII: %s", copy_buffer); + + int length = 0; + for (int i = 0; buffer[i] != 0; ){ + int width = width_from_start_byte(buffer[i]); + length = length + width; + i = i + width; + } + printf("Length in bytes: %d\n", length); + printf("Number of code points: %d\n", utf8_strlen(buffer)); + + //byte per code point + printf("Bytes per code point: "); + for (int i = 0; buffer[i] != 0;) + { + int width = width_from_start_byte(buffer[i]); + if (width == -1) + { + printf("Error in string.\n"); + return -1; + } + printf("%d ", width); + i = i + width; + } + printf("\n"); + + //substring + //careful for the range + //get help from the tutor + char result[50]; + utf8_substring(buffer, 0, 6, result); + printf("Substring of the first 6 code points: %s\n", result); + + //code point as decimal numbers + printf("Code points as decimal numbers: "); + int cp_index = 0; + for (int i = 0; buffer[i] != 0;) + { + int width = width_from_start_byte(buffer[i]); + if (width == -1) + { + printf("Error in string.\n"); + return -1; + } + + printf("%d ", codepoint_at(buffer, cp_index)); + i += width; + cp_index = cp_index + 1; + } + printf("\n"); + + + //is animal emoji + // have to use the byte index to print, NOT the CODEPOINT + printf("Animal emojis: "); + int cp_index_2 = 0; + for (int i = 0; buffer[i] != 0;) + { + int width = width_from_start_byte(buffer[i]); + + if (width == -1) + { + printf("Error in string.\n"); + return -1; + } + if (is_animal_emoji_at(buffer, cp_index_2) == 1) + { + // Find the byte index of the current codepoint + int byte_index = codepoint_index_to_byte_index(buffer, cp_index_2); + + // print multiple byte of the emoji + for (int j = 0; j < width; j++) + { + printf("%c", buffer[byte_index + j]); + } + + + } + + i += width; + cp_index_2 = cp_index_2 + 1; + } + + + + return 0; + + // Enter a UTF-8 encoded string: My 🐩’s name is Erdős. + // Valid ASCII: false + // Uppercased ASCII: "MY 🐩’S NAME IS ERDőS." + // Length in bytes: 27 + // Number of code points: 21 + // Bytes per code point: 1 1 1 4 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 + // Substring of the first 6 code points: "My 🐩’s" + // Code points as decimal numbers: 77 121 32 128041 8217 115 32 110 97 109 101 32 105 115 32 69 114 100 337 115 46 + // Animal emojis: 🐩 +} \ No newline at end of file diff --git a/tests/my_test.txt b/tests/my_test.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/my_test.txt @@ -0,0 +1 @@ +hello diff --git a/tests/my_test.txt.expect b/tests/my_test.txt.expect new file mode 100644 index 0000000..d516a4f --- /dev/null +++ b/tests/my_test.txt.expect @@ -0,0 +1,9 @@ +Enter a UTF-8 encoded string: My name is Junrui😀 I love my 🐕! +Valid ASCII: false +Uppercased ASCII: MY NAME IS JUNRU😀 I LOVE MY 🐕! +Length in bytes: 38 +Number of code points: 32 +Bytes per code point: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 1 1 1 4 1 1 +Substring of the first 6 code points: My nam +Code points as decimal numbers: 77 121 32 110 97 109 101 32 105 115 32 74 117 110 114 117 128512 32 73 32 108 111 118 101 32 32 109 121 32 128021 33 10 +Animal emojis: 🐕 \ No newline at end of file diff --git a/utfanalyzer b/utfanalyzer new file mode 100755 index 0000000000000000000000000000000000000000..d49a292f9c15edf594f74a33e619bb9b5e93b505 GIT binary patch literal 17248 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 JrQj-x{{oOhc$@$L literal 0 HcmV?d00001