ไบต์ / ตัวอักษร


28

งาน

รับสตริง UTF-8 (โดยวิธีการใด ๆ ) คำตอบ (โดยวิธีการใด ๆ ) รายการเทียบเท่าที่ทุกองค์ประกอบเป็นจำนวนไบต์ที่ใช้ในการเข้ารหัสตัวอักษรที่สอดคล้องกัน

ตัวอย่าง

!1

Ciao1 1 1 1

tʃaʊ1 2 1 2

Adám1 1 2 1

ĉaŭ2 1 2(ตัวอักษรเดียว)

ĉaŭ1 2 1 1 2(ใช้การรวมภาพซ้อนทับ)

チャオ3 3 3

(อินพุตว่าง) → (เอาต์พุตว่าง)

!±≡𩸽1 2 3 4

(ไบต์ว่าง) → 1

ไบต์ Null

หากวิธีเดียวที่จะทำให้อินพุตการอ่านเกินกว่า null ไบต์ก็คือการทราบจำนวนไบต์ทั้งหมดคุณอาจได้รับจำนวนไบต์ด้วยวิธีใด ๆ (แม้กระทั่งอินพุตของผู้ใช้)

หากภาษาของคุณไม่สามารถจัดการค่า null ได้คุณอาจจะถือว่าค่าอินพุตนั้นไม่มีค่า Null


1
หากอินพุตว่างเปล่าเราสามารถส่งออก 0 หรือค่าเท็จอื่นได้หรือไม่?
Alex A.

2
ฉันสามารถพิมพ์จำนวนไบต์โดยไม่มีการแยกได้หรือไม่? ค่าสูงสุดที่เป็นไปได้คือ 6 ดังนั้นมันจึงไม่คลุมเครือ
Dennis

3
เราจำเป็นต้องสนับสนุน null bytes หรือไม่? สิ่งเหล่านี้อาจเป็นความเจ็บปวดอย่างแท้จริงในบางภาษา ...
Dennis

3
คุณควรเพิ่มเข้าไปในโพสต์ ฉันไม่รู้ภาษาส่วนใหญ่ดีพอที่จะบอกได้ว่ามันสร้างความแตกต่าง แต่ฉันคิดว่ามันเป็นโมฆะอย่างน้อยสองคำตอบ
Dennis

2
@ Adámใช่มันจะ ใน C ตัวอย่างเช่นสตริง C ลงท้ายด้วย NUL ไบต์ดังนั้นคุณหยุดอ่านทันทีที่คุณพบ หากคุณทราบความยาวของสตริงคุณจะหยุดอ่านหลังจากนั้นหลายไบต์ NUL และทั้งหมด
แมว

คำตอบ:


10

Pyth, 9 7 ไบต์

ขอบคุณ @Maltysen สำหรับการบันทึก 2 ไบต์!

mlc.Bd8

ชุดทดสอบ

แปลงอักขระทุกตัวของอินพุตให้เป็นตัวแทนแบบไบนารี่แล้วแยกส่วนนี้เป็นส่วนที่มีความยาว 8 จำนวนชิ้นเหล่านั้นจะเป็นจำนวนไบต์ที่จำเป็นในการเข้ารหัสอักขระนั้น


1
คุณสามารถบันทึก 2 ไบต์ด้วยการแยกแทนการหารแล้วลบ.E pyth.herokuapp.com/…
Maltysen

@Maltysen มันฉลาดมากขอบคุณ!
Denker

1
คำตอบที่มีความยาวเท่ากันซึ่งต้องอาศัยกลอุบายที่คล้ายกัน:mlhc8.B
FryAmTheEggman

@LeakyNun แล้วมันจะเป็นเรื่องง่ายที่จะให้กรณีทดสอบที่ล้มเหลวใช่ไหม?
ลด

หากต้องการบันทึกไบต์อื่นแทนที่จะแบ่งเป็นชิ้นละ 8 ให้ใช้ทุก ๆ 8: ml%8.B(ตอนนี้dคือโดยปริยาย)
Anders Kaseorg


11

C, 68 65 ไบต์

b;main(c){for(;~c;b=c/64^2?b?putchar(b+48)/48:1:b+1)c=getchar();}

ขอบคุณ @FryAmTheEggman สำหรับการเล่นกอล์ฟ 3 ไบท์!

ทดสอบบนIdeone


11

APL, 15 ตัวอักษร

≢¨'UTF-8'∘⎕ucs¨

ในภาษาอังกฤษ: แปลงอักขระแต่ละตัวเป็น UTF-8 (ความหมาย: เวกเตอร์ของการแทนค่าไบต์) และรับนับ


บันทึก byte:≢¨'UTF-8'∘⎕ucs¨
Adám

แน่นอน @ Adám ... ไชโย
lstefano

วิธีที่ใช้อาร์เรย์ที่น่าสนใจ (แต่นานกว่า):+⌿0 7 11 16∘.≤2⍟⎕UCS
2560

เวอร์ชัน 16.0:0 7 11 16⍸2⍟⎕UCS
2560

7

GolfScript ขนาด 16 ไบต์

{64/2=}%1,/{,)}*

ลองออนไลน์!

พื้นหลัง

GolfScript ไม่มีเงื่อนงำว่า Unicode คืออะไร สตริงทั้งหมด (อินพุตเอาต์พุตภายใน) ประกอบด้วยไบต์ ในขณะที่อาจจะน่ารำคาญสวยมันเหมาะสำหรับความท้าทายนี้

UTF-8 เข้ารหัสอักขระ ASCII และไม่ใช่ ASCII แตกต่างกัน:

  • จุดรหัสทั้งหมดด้านล่าง 128 0xxxxxxxจะถูกเข้ารหัส

  • ทุกจุดรหัสอื่น ๆ 11xxxxxx 10xxxxxx ... 10xxxxxxจะถูกเข้ารหัสเป็น

ซึ่งหมายความว่าการเข้ารหัสของอักขระ Unicode แต่ละตัวมี0xxxxxxxไบต์เดียวหรืออย่างใดอย่างหนึ่ง11xxxxxxไบต์และ 1 ถึง 5 10xxxxxxไบต์

โดยการหารไบต์ทั้งหมดของการป้อนข้อมูลโดย64เราหัน0xxxxxxxเข้า0หรือ1 , 11xxxxxxเข้าไปใน3และ10xxxxxxออกเป็น2

ถ้าเราเปรียบเทียบความฉลาดกับ2 - ผลัก1สำหรับ2 ; และ0สำหรับ0 , 1และ3 - ตัวละครแต่ละตัวจะกลายเป็น0ตามด้วยที่จะ 5 1 1 's

สิ่งที่เหลือคือการแยกสตริงผลลัพธ์ที่เกิดขึ้นเป็น0นับจำนวน 1ระหว่างศูนย์เหล่านั้นและเพิ่มหนึ่งจำนวน

มันทำงานอย่างไร

{     }%          Map the following over all bytes in the input.
 64/                Divide the byte by 64.
    2=              Compare the quotient with 2, pushing 1 or 0.
        1,        Push range(1), i.e., [0].
          /       Split the array of Booleans around zeroes.
           {  }*  Fold; for each run of ones but the first:
            ,       Push its length.
             )      Increment.

6

PowerShell v4, 58 ไบต์

[char[]]$args[0]|%{[Text.Encoding]::UTF8.GetByteCount($_)}

NB

ตกลงสิ่งนี้ควรใช้งานได้และทำในเกือบทุกกรณีทดสอบยกเว้นกรณี𩸽ใดก็ตามที่มีการนับ3,3ในเครื่องของฉัน ตัวละครนั้นแม้จะแสดงเป็น7 ไบต์บนคอมพิวเตอร์ของฉัน ฉันสงสัยว่าเกิดจากข้อผิดพลาดบางอย่างใน Windows หรือ. NET รุ่นที่ฉันใช้ในเครื่องเนื่องจาก @Mego ไม่มีปัญหานั้น ( แก้ไข: @cat ชี้ให้เห็นว่านี่เป็นเพราะBOMขอบคุณสำหรับการไขปริศนานั้น @cat! )

อย่างไรก็ตามนั่นยังไม่ได้พิจารณาปัญหาทั้งหมด ฉันคิดว่าฉันรู้ว่าปัญหาบางอย่างมาจากไหน ภายใน. NET สตริงทั้งหมดประกอบด้วยหน่วยรหัส UTF-16 (ซึ่งเป็นชนิด System.Char) ด้วย Typecasting หลวมมากที่ใช้ PowerShell มีเป็นจำนวนมากของการหล่อโดยปริยายและการแปลงระหว่างชนิดในพื้นหลัง แนวโน้มนี้เป็นปัจจัยสนับสนุนต่อพฤติกรรมที่เราเห็นตัวอย่างเช่น[system.text.encoding]::utf8.getchars([System.Text.UTF8Encoding]::UTF8.GetBytes('𩸽'))ส่งคืนสอง unprintables แทนที่จะเป็นอักขระเดียว


คำอธิบาย

รหัสตรงไปตรงมามาก ใช้เวลาการป้อนข้อมูลและชัดเจนดุจเป็นถ่านอาร์เรย์เพื่อให้เราสามารถห่วงผ่านองค์ประกอบของสตริงแต่ละ$args[0] |%{...}แต่ละซ้ำเราจะใช้โทร .NET [System.Text.Encoding]::UTF8.GetByteCount()(คนSystem.บอกเป็นนัย ๆ ) $_จะได้รับการนับไบต์ของตัวละครในปัจจุบัน ที่วางไว้บนท่อเพื่อการส่งออกในภายหลัง เนื่องจากมันเป็นคอลเลกชันของ[int]s ที่คืนค่าการส่งไปยังอาเรย์จึงเป็นนัย

ทดสอบการทำงาน

PS C:\Tools\Scripts\golfing> .\bytes-per-character.ps1 'tʃaʊ'
1
2
1
2

PS C:\Tools\Scripts\golfing> .\bytes-per-character.ps1 'Adám'
1
1
2
1

PS C:\Tools\Scripts\golfing> .\bytes-per-character.ps1 'ĉaŭ'
2
1
2

PS C:\Tools\Scripts\golfing> .\bytes-per-character.ps1 'ĉaŭ'
1
2
1
1
2

PS C:\Tools\Scripts\golfing> .\bytes-per-character.ps1 'チャオ'
3
3
3

PS C:\Tools\Scripts\golfing> .\bytes-per-character.ps1 '!±≡𩸽'
1
2
3
3
3

แก้ไขเพื่อเพิ่มสิ่งนี้ดำเนินการอย่างถูกต้องสำหรับข้อกำหนด null-bytes ที่เพิ่มเข้ากับความท้าทายหลังจากที่ฉันโพสต์ครั้งแรกโดยที่คุณดึงข้อมูลจากไฟล์ข้อความและไพพ์ดังต่อไปนี้:

PS C:\Tools\Scripts\golfing> gc .\z.txt -Encoding UTF8|%{.\bytes-per-character.ps1 $_}
2
1
1
1

z.txt


That character even shows as 7 bytes on my computer.ใช่นั่นเป็นเพราะByte-Order Markซึ่งเป็นสิ่งที่คุณได้รับบน Windows ด้วย UTF-8 บอกให้ Notepad ++ ใช้UTF-8 without BOM(ตามที่คุณควรหลีกเลี่ยง BOMโดยเฉพาะอย่างยิ่งสำหรับความเข้ากันได้กับ Unicies) และคุณจะพบว่าไฟล์มีขนาด 4 ไบต์เนื่องจาก BOM เป็น 3 และ 4 + 3 = 7
cat

@cat Ah ใช่ว่าเหมาะสมแล้ว ตกลงดังนั้นบัญชีสำหรับความแตกต่างในขนาดไฟล์ อย่างไรก็ตามยังไม่ได้อธิบายถึงพฤติกรรมที่แตกต่างภายในเชลล์ ยกตัวอย่างเช่นบันทึกเป็น UTF-8 โดยไม่ต้อง BOM และการทำงานยังคงให้ผลตอบแทนget-content -Encoding UTF8 .\z.txt|%{.\bytes-per-character.ps1 $_} 3,3
AdmBorkBork

1
-Encodingพารามิเตอร์ไม่ปรากฏว่าได้รับการสนับสนุน
Mego


6

JavaScript (ES6), 54 45 43 ไบต์

s=>[...s].map(c=>encodeURI(c).length/3-8&7)

แก้ไข: บันทึก 2 ไบต์ด้วยความช่วยเหลือจาก @ l4m2


s=>[...s].map(c=>encodeURI(c).length/3-4&3)
l4m2

@l4m2 That fails for non-BMP characters but I was able to fix it up.
Neil


5

Perl 6,  77 69  63 bytes

put +$0 if $_».base(2).fmt("%8d")~~/^(1)**2..*|^(" ")/ while $_=$*IN.read: 1
put +$0 if $_».fmt("%8b")~~/^(1)**2..*|^(" ")/ while $_=$*IN.read: 1

put 1+$0 if $_».fmt("%8b")~~/^1(1)+|^" "/while $_=$*IN.read: 1
put 1+$0 if $_».fmt("%0.8b")~~/^1(1)+|^0/while $_=$*IN.read: 1

Since Perl 6 uses NFG strings I have to pull in the bytes directly, which sidesteps the feature.
(NFG is like NFC except it also creates synthetic composed codepoints)

The output is separated by newlines.

Test:

for text in '!' 'Ciao' 'tʃaʊ' 'Adám' 'ĉaŭ' 'ĉaŭ' 'チャオ' '' '!±≡𩸽' '𩸽\0𩸽';
do
  echo -en $text |
  perl6 -e 'put 1+$0 if $_».fmt("%8b")~~/^1(1)+|^" "/while $_=$*IN.read: 1' |

  # combine all of the lines into a single one for display purposes
  env text=$text perl6 -e 'put qq["%*ENV<text>"], "\t\t", lines.gist'
done
"!"     (1)
"tʃaʊ"      (1 2 1 2)
"Adám"      (1 1 2 1)
"ĉaŭ"       (2 1 2)
"ĉaŭ"     (1 2 1 1 2)
"チャオ"       (3 3 3)
""      ()
"!±≡𩸽"     (1 2 3 4)
"𩸽\0𩸽"        (4 1 4)

Explanation:

# turns the list in 「$0」 into a count, and adds one
# 「put」 prints that with a trailing newline
put 1+$0 

   # if the following is true
   if

       # format the input byte to base 2 and pad it out to 8 characters
       $_».fmt("%8b")

       ~~ # smart match against

       # check to see if it starts with more than one 1s, or a space
       # ( also sets 「$0」 to a list that is 1 shorter
       # than the number of bytes in this codepoint )
       / ^1 (1)+ | ^" " /

           # for every byte in STDIN
           while
               $_ = $*IN.read: 1

This works because the first byte in a multi-byte codepoint has the number of bytes encoded inside of it, and the other bytes in the codepoint have the highest bit set, but not the next highest. While the single byte codepoints don't have the highest bit set.


Can't do read:1 and/or /while$ instead? And if that works, if$?
Erik the Outgolfer

@EʀɪᴋᴛʜᴇGᴏʟғᴇʀ No because that would be parsed as something different. I can remove the space before while though.
Brad Gilbert b2gills

Can you explain the NFG countermeasures?
JDługosz

If I echo a NUL byte to this program's STDIN, it prints \n1\n1\n, is that intentional? Basically, does this handle NUL bytes?
cat

@cat Why wouldn't it? When I do this: perl -e 'print "𩸽\0𩸽"' | perl6 -e '...' I get 4␤1␤4 just like I would expect. ( The part about nuls was added after I posted though )
Brad Gilbert b2gills

5

Python 3, 82 bytes

import math
lambda x:[ord(i)<128and 1or int((math.log2(ord(i))-1)//5+1)for i in x]

This is much longer than the other Python answer, and the majority of the other answers, but uses an approach involving logarithms that I haven't yet seen.

An anonymous function that takes input, via argument, as a string and returns a list.

Try it on Ideone

How it works

This method relies on the way in which UTF-8 encodes the code-point of a character. If the code-point is less than 128, the character is encoded as in ASCII:

0xxxxxxx

where x represents the bits of the code point. However, for code-points greater than or equal to 128, the first byte is padded with the same number of 1 s as the total number of bytes, and subsequent bytes begin 10. The bits of the code-point are then entered to give the shortest possible multibyte sequence, and any remaining bits become 0.

No. of bytes  Format
1             0xxxxxxx
2             110xxxxx 10xxxxxx
3             1110xxxx 10xxxxxx 10xxxxxx
4             11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
...           ...

and so forth.

It can now be noticed that for each number of bytes n, the upper limit for the number of code-point bits is given by (-n+7)+6(n-1) = 5n+1. Hence, the upper limit code-point c for each n is given, in decimal, by c= 2^(5n+1). Rearranging this gives n = (log2(c)-1)/5. So for any code-point, the number of bytes can be found by evaluating the above expression, and then taking the ceiling.

However, this does not work for code points in the range 64 <= c <= 127, since the lack of a padding 1 due to the ASCII-like encoding for 1 byte characters means that the wrong upper limit is predicted, and log2 is undefined for c = 0, which happens if a null byte is present in the input. Therefore, if c <= 127, a value of 1 is returned for n.

This is exactly what the code is doing; for each character i in the string x, the code-point is found using the ord function, and the ceiling of the expression is found by using integer rather than float division by 5 and then adding 1. Since Python's float type always represents integers as x.0, even after integer division, the result is passed to the int function to remove the trailing zero. If ord(i) <= 127, logical short-circuiting means that 1 is instead returned. The number of bytes for each character is stored as an element in a list, and this list is returned.


5

Java 10, 100 96 95 67 61 bytes

a->{for(var c:a)System.out.print(c.getBytes("utf8").length);}

-4 bytes removing spaces because this is allowed in the comments
-1 byte changing UTF-8 to utf8
-28 bytes going from Java 7 to 8 (a->{...} instead of void c(char[]i)throws Exception{...})
-3 bytes taking the input as String-array instead of character-array, and
-3 bytes going from Java 8 to 10 (var instead of String)

Explanation:

Try it online.

a->{                      // Method with String-array parameter and no return-type
  for(var c:a)            //  Loop over the input-array
    System.out.print(     //   Print:
      c.getBytes("utf8")  //    The bytes as array in UTF-8 of the current item,
       .length);}         //    and print the amount of bytes in this array

Does it work for null bytes?
cat

@cat The test case for null-bytes was later added. But yes, it does also work for null-bytes and I've added the test case.
Kevin Cruijssen

3

Julia, 34 bytes

s->s>""?map(sizeof,split(s,"")):[]

This is an anonymous function that accepts a string and returns an integer array. To call it, assign it to a variable.

The approach is quite straightforward: If the input is empty, the output is empty. Otherwise we map the sizeof function, which counts the number of bytes in a string, to each one-character substring.

Try it online! (includes all test cases)


s->[sizeof("$c")for c=s] saves a few bytes.
Dennis

Odd; does split("","") not return []? (JavaScript's "".split("") does.)
Neil

@Neil split("","") appears to give "" (unlike in Python which gives an exception) but I don't know anything about the compatibility of [] and "" in julia.
cat

@Neil No, split("", "") == [""], i.e. a one-element array containing an empty string, but the issue is that sizeof("") == 0, which the OP said is not allowed.
Alex A.

@Dennis That will fail for non-indexable strings. (Can't think of an example offhand though.)
Alex A.

3

PHP, 92 57 bytes

On second thought you can do this with much less faffing around:

<?php for(;$a=strlen(mb_substr($argv[1],$i++,1));)echo$a;

Try it online note that this is slightly longer as it uses stdin rather than a program argument.
This version requires you to ignore notices sent to stderr but that's fine.

old version:
Uses a rather different approach to the other php answer. Relies on the lack of native support for multi-byte strings in php.

<?php for($l=strlen($a=$argv[1]);$a=mb_substr($a,1);$l=$v)echo$l-($v=strlen($a));echo$l?:'';

Nice answer! I think you can drop the opening tag entirely, or change it to <?=
cat

Without the tag it's a code snippet rather than a program, and even if that's allowed it makes me feel vaguely dirty. With the alternate tag you get a parse error (or at least I did on php 5.5 which is what I'm used to).
user55641

Okay :) I don't know PHP (nor do I want to, cough) but I'll point you here: codegolf.stackexchange.com/questions/2913
cat

3

Emacs Lisp, 55 49 bytes

(lambda(s)(mapcar'string-bytes(mapcar'string s)))

First dissects the string into a list of characters with (mapcar 'string s). The string function in Emacs Lisp takes a list of characters and builds a string out of them. Due to the way Emacs splits strings with mapcar (i.e. into a list of integers, not characters or strings), this explicit conversion is needed. Then maps the string-bytes function onto that list of strings.

Example:

(mapcar 'string "abc") ; => ("a" "b" "c")
(mapcar 'string-bytes '("a" "b" "c")) ; => (1 1 1) 

Testcases:

(mapcar
 (lambda(s)(mapcar'string-bytes(mapcar'string s)))
 '("!""Ciao""tʃaʊ""Adám""ĉaŭ""ĉaŭ""チャオ""""!±≡𩸽""\0"))
;; ((1) (1 1 1 1) (1 2 1 2) (1 1 2 1) (2 1 2) (1 2 1 1 2) (3 3 3) nil (1 2 3 4) (1))

Old answer:

(lambda(s)(mapcar(lambda(s)(string-bytes(string s)))s))

Ungolfed:

 (lambda (s)
   (mapcar
    ;; we can't use string-bytes directly,
    ;; since Emacs mapcar yields a list of ints instead of characters
    ;; therefore we need a wrapper function here. 
    (lambda (s)
      (string-bytes (string s)))
    s))

Testcases:

(mapcar
 (lambda(s)(mapcar(lambda(s)(string-bytes(string s)))s))
 '("!""Ciao""tʃaʊ""Adám""ĉaŭ""ĉaŭ""チャオ""""!±≡𩸽""\0"))
;; ((1) (1 1 1 1) (1 2 1 2) (1 1 2 1) (2 1 2) (1 2 1 1 2) (3 3 3) nil (1 2 3 4) (1))


What happens to the nil if you flattens the result?
Adám

1
@Adám nil is an empty list (and the only way to say "false" in Emacs). While there is no standard flatten in Emacs (you can use dash's -flatten) any possible implementation would eliminate it.
Lord Yuuma

3

JavaScript (Node), 27 bytes

s=>s.map(Buffer.byteLength)

This takes input as an array of individual characters, and returns an array of byte counts.

Buffer is a method of representing raw binary data. Buffer.byteLength(string) gives the number of bytes in the string. UTF-8 is the default encoding. Note that only Node.js has buffers, not browser JS. The rough browser equivalent is called Blob, which comes in at 31 bytes:

s=>s.map(e=>new Blob([e]).size)

Test

Save this file and run it through node, or try it online.

var f =
  s=>s.map(Buffer.byteLength)

var tests = [
  ["!"],
  ["C","i","a","o"],
  ["t","ʃ","a","ʊ"],
  ["A","d","á","m"],
  ["ĉ","a","ŭ"],
  ["c","̂","a","u","̆"],
  ["チ","ャ","オ"],
  [],
  ["!","±","≡","𩸽"]
];

tests.forEach(test => {
  console.log(test, f(test));
});

This should be the result:

$ node bytes.js
[ '!' ] [ 1 ]
[ 'C', 'i', 'a', 'o' ] [ 1, 1, 1, 1 ]
[ 't', 'ʃ', 'a', 'ʊ' ] [ 1, 2, 1, 2 ]
[ 'A', 'd', 'á', 'm' ] [ 1, 1, 2, 1 ]
[ 'ĉ', 'a', 'ŭ' ] [ 2, 1, 2 ]
[ 'c', '̂', 'a', 'u', '̆' ] [ 1, 2, 1, 1, 2 ]
[ 'チ', 'ャ', 'オ' ] [ 3, 3, 3 ]
[] []
[ '!', '±', '≡', '�' ] [ 1, 2, 3, 4 ]

3

Bash, 74 bytes

Golfed

xxd -p|fold -2|cut -c1|tr -d '89ab'|echo `tr -t '01234567cbef' '[1*]2234'`

Algorithm

hexdump input string, fold 2 chars per line, cut the first char only

echo -ne '!±≡𩸽' | xxd -p|fold -2|cut -c1

2
c
b
e
8
a
f
a
b
b

(4 high order bits of an each input byte as a hex char, one per line)

Remove "continuation bytes" 0x80..0xBF

tr -d '89ab'

2
c

e


f

(what is left, is 4 bits of the first byte of an each unicode char)

map the first bits into the char length, collapse the output and print

echo `tr -t '01234567cbef' '[1*]2234'`

1 2 3 4

Test

 U() { xxd -p|fold -2|cut -c1|tr -d '89ab'|echo `tr -t '01234567cbef' '[1*]2234'`;}

 echo -ne '!' | U 
 1

 echo -ne 'Ciao' | U
 1 1 1 1

 echo -ne 'tʃaʊ' | U
 1 2 1 2

 echo -ne 'Adám' | U
 1 1 2 1

 echo -ne 'ĉaŭ' | U
 2 1 2

 echo -ne 'ĉaŭ' | U
 1 2 1 1 2

 echo -ne 'チャオ' | U
 3 3 3
 echo -ne '!±≡𩸽' | U
 1 2 3 4

 echo -ne "\x0" | U
 1

 echo -ne '' | U

+1 Nice approach. You actually read the result directly from the input.
Adám

The -t option to tr was unfamiliar to me, and is apparently a GNU extension. Piping to the command substitution after echo might also be worth a slightly more detailed explanation.
tripleee


2

C#, 89 82 bytes

I=>{var J="";foreach(char c in I){J+=Encoding.UTF8.GetByteCount(c+"");}return J;};

A simple C# lambda that iterates through the string and returns the space separated list.

Edit: saved 6 bytes thanks to some very nice comments.


pretty sure you can do var J="";...
cat

Also, the OP states in a comment that you do not need to space-separate the output so 1121 and 1 2 1 2 are both OK
cat

1
@cat Thanks, saved me 6 bytes
AstroDan

Also, you have an extra space in } return J;};
cat

Seems like you need to using System.Text or thereabouts -- imports are not free.
cat



1

C, 85 bytes.

l(unsigned char* c){while(*c){int d=(*c>>4)-11;
d=d<0?1:d+(d==1);putchar(48+d);c+=d;}}

Examines the high 4 bits of each byte to determine the encoding and the number of subsequent bytes to skip;


Does this work on null bytes?
cat

Yes, the while *c exits on an empty string, and the `c+=d' skips nulls in the middle of a multi byte codepoint.
AShelly

1
That's incorrect. The end of a string (char*, really) in C is marked with a null byte. It is impossible to distinguish null bytes from the actual end of the string.
Dennis

@Dennis Precisely because there is no difference :)
cat

1
The OP stated in a comment (and it's now in the post) that you can request the length of the string in bytes as an argument, so do that and this will be valid again
cat

1

Factor, 57 87 82 80 bytes

[ [ dup zero? [ drop "1"] [ >bin length 4 /i 10 >base ] if ] { } map-as ""join ]

Explained:

USING: kernel math math.parser sequences ;
IN: byte-counts

: string>byte-counts ( str -- counts )
  [                  ! new quotation: takes a char as a fixnum
    dup zero?        ! true if this is a NUL byte
    [ drop "1" ]     ! NUL bytes have length 1
    [ >bin           ! else, convert to binary string
      length         ! length of binary string
      4              ! the constant 4
      /i             ! integer division
      number>string  ! 4 -> "4"
    ] if             ! conditionally execute one of the previous quotations
  ]                  ! end
  { } map-as         ! map and clone-like an { } array
  "" join ;          ! join array of 1strings on empty string

Unit tests:

USING: tools.test byte-counts ;
IN: byte-counts.tests

{ "1" } [ "!" string>byte-counts ] unit-test
{ "1111" } [ "Ciao" string>byte-counts ] unit-test
{ "1212"} [ "tʃaʊ" string>byte-counts ] unit-test
{ "1121" } [ "Adám" string>byte-counts ] unit-test
{ "212" } [ "ĉaŭ" string>byte-counts ] unit-test
{ "12112" } [ "ĉaŭ" string>byte-counts ] unit-test
{ "333" } [ "チャオ" string>byte-counts ] unit-test
{ "" } [ "" string>byte-counts ] unit-test
{ "1234" } [ "!±≡𩸽" string>byte-counts ] unit-test
{ "1" } [ "\0" string>byte-counts ] unit-test

They all pass, now. c:


1

Swift 2.2, 67 52 50 bytes

for c in i.characters{print(String(c).utf8.count)}

Horribly ugly. There's no way to get the UTF-8 length of a Character in Swift, so I need to iterate through the string by character, convert the Character to a String, and find the count of that single-character String (hey, at least there's a built-in method to do that). Looking for optimizations, possibly using a scanner.

Revision 1: Saved 15 bytes by using count instead of underestimateCount().

Revisions 2: Saved another 2 character by using a for-in loop instead of a for each closure.


1

Rust, 53 bytes

|s:&str|for c in s.chars(){print!("{}",c.len_utf8())}

Rust has utf-8 char primitives, iterators, and lambdas, so this was straightforward. Test code:

fn main() {
    let s = "Löwe 老虎 Léopard💖💖💖💖";
    let f =|s:&str|for c in s.chars(){print!("{}",c.len_utf8())};
    f(s);
}

Outputs

1211133112111114444 

1

jq, 26 characters

(23 characters code + 3 characters command line option)

(./"")[]|utf8bytelength

Hopefully competing. Although utf8bytelength was added 9++ months before this question, it is still not included in released version.

Sample run:

bash-4.3$ ./jq -R '(./"")[]|utf8bytelength' <<< 'tʃaʊ'
1
2
1
2

bash-4.3$ ./jq -R '(./"")[]|utf8bytelength' <<< 'ĉaŭ '
1
2
1
1
2
1

bash-4.3$ ./jq -R '(./"")[]|utf8bytelength' <<< 'チャオ'
3
3
3

bash-4.3$ ./jq -R '(./"")[]|utf8bytelength' <<< ''

bash-4.3$ ./jq -R '(./"")[]|utf8bytelength' <<< '!±≡𩸽'
1
2
3
4


1

SmileBASIC, 69 bytes

DEF C B
WHILE I<LEN(B)Q=INSTR(BIN$(B[I],8),"0")I=I+Q+!Q?Q+!Q
WEND
END

Input is an array of bytes.

The number of bytes in a UTF-8 character is equal to the number of leading 1 bits in the first byte (unless there are no 1s, in which case the character is 1 byte). To find the number of leading 1s, the program finds the first 0 in the binary representation, then adds 1 if this was 0.

0xxxxxxx - no leading ones, 1 byte
110xxxxx 10xxxxxx - 2 leading ones, 2 bytes
1110xxxx 10xxxxxx 10xxxxxx - 3 leading ones, 3 bytes
etc.

1

F#, 59 54 66 bytes

(s)=seq{for c in s->System.Text.Encoding.UTF8.GetByteCount([|c|])}

Technically, s is a char sequence, but it turns out there's an implicit conversion that allows a string to be passed in.

When testing this in the console with !±≡𩸽, it splits the kanji into two characters, each 3 bytes long. All the other test cases work fine.

Edit: It turns out common namespace imports are not implicit. Up another 12 chars.


1) Timmy D's powershell answer has the same 6-bytes-per-kanji problem. I would attribute it to Windows being dumb and useless at Unicode. 2) If you get 6 bytes for the kanji when reading from a file enocded with UTF-8 without BOM then this is wrong and should be fixed. 3) Seems like F# needs statements like let f(x)= ... to end in ;;, like SML. 4) You can leave off assigning this anonymous function a name, i.e. (s)=seq{for c in s->Encoding.UTF8.GetByteCount([|c|])}.
cat

Also, I get error FS0039: The namespace or module 'Encoding' is not defined when trying to run this. What am I doing wrong?
cat

Also, welcome to Programming Puzzles and Code Golf, this is a nice first answer! :D
cat

@cat You need to open the System.Text namespace. I'm assuming namespace opens and entry code are included, coming from AstroDan's C# answer.
sealed interface

You need to count the bytes of any import, #include, open, load, require, using, USING: etc here on PPCG. AstroDan's C# answer is similarly erroneous, and I notified them of that.
cat

1

05AB1E, 15 bytes

ÇεDžy‹i1ë.²<5÷>

Try it online.
Header ε is used to for-each over all the test cases;
Footer ï]J]» to pretty-print the output character-lists (ï: decimals and characters to integers; ]: close if-else and for-each; J: Join digits together; }: close header foreach; »: Join by new-lines).

Explanation:

Ç                   # Convert each character to its unicode value
 εD                 # Foreach over this list
      i             #  If the current item
     ‹              #  is smaller than
   žy               #  128
       1            #   Use 1
        ë           #  Else
         .²         #   Use log_2
           <        #   minus 1
            5÷      #   integer-divided by 5
              >     #   plus 1

Since 05AB1E doesn't have any builtins to convert characters to amount of bytes used, I use Ç to convert the characters to their unicode values, and in a for-each do the following in pseudo-code:

if(unicodeValue < 128)
  return 1
else
  return log_2(unicodeValue-1)//5+1    # (where // is integer-division)

Inspired by @TheBikingViking's Python 3 answer.


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.