เหตุใดอักขระอิโมจิเช่น 👩‍👩‍👧‍👦 จึงมีความแปลกประหลาดในสายของสวิฟท์


540

อักขระ 👩‍👩‍👧‍👦 (ครอบครัวที่มีผู้หญิงสองคนผู้หญิงหนึ่งคนและเด็กชายหนึ่งคน) ถูกเข้ารหัสเช่นนี้:

U+1F469 WOMAN,
‍U+200D ZWJ,
U+1F469 WOMAN,
U+200D ZWJ,
U+1F467 GIRL,
U+200D ZWJ,
U+1F466 BOY

ดังนั้นจึงมีการเข้ารหัสที่น่าสนใจมาก เป้าหมายที่สมบูรณ์แบบสำหรับการทดสอบหน่วย อย่างไรก็ตาม Swift ดูเหมือนจะไม่รู้วิธีจัดการกับมัน นี่คือสิ่งที่ฉันหมายถึง:

"👩‍👩‍👧‍👦".contains("👩‍👩‍👧‍👦") // true
"👩‍👩‍👧‍👦".contains("👩") // false
"👩‍👩‍👧‍👦".contains("\u{200D}") // false
"👩‍👩‍👧‍👦".contains("👧") // false
"👩‍👩‍👧‍👦".contains("👦") // true

ดังนั้นสวิฟท์บอกว่ามันมีตัวเอง (ดี) และเด็กผู้ชาย (ดี!) แต่มันก็บอกว่ามันไม่ได้มีผู้เข้าร่วมหญิงสาวหรือศูนย์กว้าง เกิดอะไรขึ้นที่นี่ เหตุใด Swift จึงรู้ว่ามีเด็กผู้ชาย แต่ไม่ใช่ผู้หญิงหรือผู้หญิง ฉันสามารถเข้าใจได้ว่ามันปฏิบัติตัวมันเป็นตัวละครตัวเดียวและรู้ตัวว่ามันบรรจุตัวมันเอง แต่ความจริงที่ว่ามันมีส่วนประกอบย่อยหนึ่งอันและไม่มีใครทำให้ฉันงง

"👩".characters.first!นี้จะไม่เปลี่ยนถ้าผมใช้สิ่งที่ต้องการ


สิ่งที่น่าสับสนยิ่งกว่านี้ก็คือ:

let manual = "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}"
Array(manual.characters) // ["👩‍", "👩‍", "👧‍", "👦"]

แม้ว่าฉันจะวาง ZWJs ไว้ในนั้น แต่มันก็ไม่ได้สะท้อนในอาเรย์ตัวละคร สิ่งที่ตามมาคือการบอกเล็กน้อย:

manual.contains("👩") // false
manual.contains("👧") // false
manual.contains("👦") // true

ดังนั้นฉันจึงมีพฤติกรรมเหมือนกันกับอาเรย์ตัวละคร ... ซึ่งน่ารำคาญอย่างยิ่งเนื่องจากฉันรู้ว่าอาเรย์เป็นอย่างไร

"👩".characters.first!นอกจากนี้ยังไม่เปลี่ยนถ้าผมใช้สิ่งที่ต้องการ



1
ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Martijn Pieters

1
แก้ไขใน Swift 4 "👩‍👩‍👧‍👦".contains("\u{200D}")ยังคงส่งคืนค่าเท็จไม่แน่ใจว่าเป็นข้อบกพร่องหรือคุณลักษณะ
เควิน

4
Yikes Unicode มีข้อความที่ถูกทำลาย มันเปลี่ยนข้อความธรรมดาเป็นภาษามาร์กอัป
Boann

6
@Boann ใช่และไม่ใช่ ... มีการเปลี่ยนแปลงมากมายในการทำ en / ถอดรหัสสิ่งต่าง ๆ เช่น Hangul Jamo (255 codepoints) ไม่ใช่ฝันร้ายที่สมบูรณ์แบบสำหรับ Kanji (codepoints 13,108) และ Chinese Ideographs (199,528 codepoints) แน่นอนว่ามันซับซ้อนและน่าสนใจกว่าความคิดเห็น SO ดังนั้นฉันจึงแนะนำให้คุณลองดูด้วยตัวคุณเอง: D
Ben Leggiero

คำตอบ:


402

สิ่งนี้เกี่ยวข้องกับวิธีการStringทำงานของประเภทใน Swift และวิธี contains(_:)การทำงาน

'👩‍👩‍👧‍👦' คือสิ่งที่เรียกว่าลำดับอิโมจิซึ่งแสดงเป็นอักขระที่มองเห็นได้หนึ่งตัวในสตริง ลำดับประกอบด้วยCharacterวัตถุและในขณะเดียวกันก็ประกอบไปด้วยUnicodeScalarวัตถุ

หากคุณตรวจสอบจำนวนตัวอักษรของสตริงคุณจะเห็นว่ามันประกอบด้วยอักขระสี่ตัวในขณะที่ถ้าคุณตรวจสอบจำนวนสเกลาร์ Unicode มันจะแสดงผลลัพธ์ที่แตกต่าง:

print("👩‍👩‍👧‍👦".characters.count)     // 4
print("👩‍👩‍👧‍👦".unicodeScalars.count) // 7

ตอนนี้ถ้าคุณแยกวิเคราะห์ตัวละครแล้วพิมพ์ออกมาคุณจะเห็นว่าดูเหมือนอักขระปกติ แต่ที่จริงแล้วตัวละครสามตัวแรกนั้นมีทั้งอิโมจิและตัวเชื่อมที่มีความกว้างเป็นศูนย์ในUnicodeScalarView:

for char in "👩‍👩‍👧‍👦".characters {
    print(char)

    let scalars = String(char).unicodeScalars.map({ String($0.value, radix: 16) })
    print(scalars)
}

// 👩‍
// ["1f469", "200d"]
// 👩‍
// ["1f469", "200d"]
// 👧‍
// ["1f467", "200d"]
// 👦
// ["1f466"]

อย่างที่คุณเห็นเฉพาะตัวละครตัวสุดท้ายที่ไม่มีตัวเชื่อมความกว้างเป็นศูนย์ดังนั้นเมื่อใช้contains(_:)วิธีนี้มันจะทำงานได้ตามที่คุณคาดหวัง เนื่องจากคุณไม่ได้เปรียบเทียบกับอิโมจิที่มีผู้เข้าร่วมที่มีความกว้างเป็นศูนย์วิธีการนี้จึงไม่สามารถหาคู่ที่ตรงกันได้

หากต้องการขยายสิ่งนี้หากคุณสร้างStringตัวอักษรที่ประกอบด้วยอักขระอิโมจิที่ลงท้ายด้วยตัวเชื่อมที่มีความกว้างเป็นศูนย์และส่งไปยังcontains(_:)เมธอดมันจะทำการประเมินfalseด้วยเช่นกัน สิ่งนี้เกี่ยวข้องกับcontains(_:)การถูกต้องเหมือนกันrange(of:) != nilซึ่งพยายามค้นหาการจับคู่ที่ตรงกันกับอาร์กิวเมนต์ที่กำหนด เนื่องจากอักขระที่ลงท้ายด้วยตัวเชื่อมความกว้างเป็นศูนย์จะสร้างลำดับที่ไม่สมบูรณ์วิธีจึงพยายามค้นหาการจับคู่สำหรับอาร์กิวเมนต์ในขณะที่รวมอักขระที่ลงท้ายด้วยตัวเชื่อมที่มีความกว้างเป็นศูนย์ไว้ในลำดับที่สมบูรณ์ ซึ่งหมายความว่าวิธีการจะไม่พบคู่ที่ตรงกันหาก:

  1. อาร์กิวเมนต์ลงท้ายด้วยตัวเชื่อมความกว้างศูนย์และ
  2. สตริงที่จะแยกวิเคราะห์ไม่มีลำดับที่ไม่สมบูรณ์ (เช่นลงท้ายด้วยตัวเชื่อมศูนย์ความกว้างและไม่ตามด้วยอักขระที่เข้ากันได้)

เพื่อสาธิต:

let s = "\u{1f469}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}" // 👩‍👩‍👧‍👦

s.range(of: "\u{1f469}\u{200d}") != nil                            // false
s.range(of: "\u{1f469}\u{200d}\u{1f469}") != nil                   // false

อย่างไรก็ตามเนื่องจากการเปรียบเทียบมองไปข้างหน้าคุณสามารถค้นหาลำดับที่สมบูรณ์อื่น ๆ ภายในสตริงโดยทำงานย้อนหลัง:

s.range(of: "\u{1f466}") != nil                                    // true
s.range(of: "\u{1f467}\u{200d}\u{1f466}") != nil                   // true
s.range(of: "\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}") != nil  // true

// Same as the above:
s.contains("\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}")          // true

ทางออกที่ง่ายที่สุดคือการให้ตัวเลือกการเปรียบเทียบที่เฉพาะเจาะจงกับrange(of:options:range:locale:)วิธีการ ตัวเลือกในการString.CompareOptions.literalดำเนินการเปรียบเทียบนั้นเท่าเทียมกันของตัวละครโดยตัวละครที่แน่นอน ในฐานะที่เป็นหมายเหตุด้านสิ่งที่มีความหมายโดยตัวละครที่นี่ไม่ใช่ Swift Characterแต่การแสดง UTF-16 ของทั้งอินสแตนซ์และสตริงการเปรียบเทียบ - อย่างไรก็ตามเนื่องจากStringไม่อนุญาต UTF-16 ที่มีรูปแบบไม่ถูกต้อง การแสดง

ที่นี่ฉันได้ใช้Foundationวิธีการมากเกินไปดังนั้นหากคุณต้องการวิธีดั้งเดิมให้เปลี่ยนชื่อสิ่งนี้หรือบางสิ่ง:

extension String {
    func contains(_ string: String) -> Bool {
        return self.range(of: string, options: String.CompareOptions.literal) != nil
    }
}

ตอนนี้วิธีการทำงานตามที่ "ควร" กับตัวละครแต่ละตัวแม้จะมีลำดับไม่สมบูรณ์:

s.contains("👩")          // true
s.contains("👩\u{200d}")  // true
s.contains("\u{200d}")    // true

47
@MartinR ตาม UTR29 ปัจจุบัน (Unicode 9.0) มันเป็นกลุ่มกราฟขยาย ( กฎ GB10 และ GB11 ) แต่ Swift ใช้รุ่นที่เก่ากว่าอย่างชัดเจน เห็นได้ชัดว่าการแก้ไขเป็นเป้าหมายสำหรับภาษาเวอร์ชัน 4ดังนั้นพฤติกรรมนี้จะเปลี่ยนแปลงในอนาคต
Michael Homer

9
@MichaelHomer: เห็นได้ชัดว่าได้รับการแก้ไขแล้ว"👩‍👩‍👧‍👦".countประเมิน1ด้วย Xcode 9 เบต้าปัจจุบันและ Swift 4
Martin R

5
ว้าว. มันยอดเยี่ยมมาก แต่ตอนนี้ฉันเริ่มคิดถึงอดีตเมื่อปัญหาที่เลวร้ายที่สุดที่ฉันมีกับสตริงคือพวกเขาใช้การเข้ารหัสสไตล์ C หรือ Pascal
Owen Godfrey

2
ฉันเข้าใจว่าทำไมมาตรฐาน Unicode อาจจำเป็นต้องสนับสนุนสิ่งนี้ แต่มนุษย์นี่เป็นระเบียบที่เกินความจริงหากมีสิ่งใด: /
Reinstate Monica

110

ปัญหาแรกคือคุณกำลังเชื่อมต่อกับมูลนิธิด้วยcontains(Swift's Stringไม่ใช่Collection) ดังนั้นนี่คือNSStringพฤติกรรมซึ่งฉันไม่เชื่อว่ามือจับที่แต่งขึ้นเป็น Emoji อย่างทรงพลังเท่ากับ Swift ที่กล่าวว่า Swift ฉันเชื่อว่ากำลังใช้ Unicode 8 ในขณะนี้ซึ่งจำเป็นต้องมีการแก้ไขสถานการณ์นี้ใน Unicode 10 (ดังนั้นสิ่งนี้อาจเปลี่ยนแปลงได้เมื่อพวกเขาใช้ Unicode 10; ฉันไม่ได้ขุดลงไปไม่ว่าจะเป็นหรือไม่ก็ตาม)

เพื่อให้สิ่งต่าง ๆ ง่ายขึ้นให้กำจัด Foundation และใช้ Swift ซึ่งให้มุมมองที่ชัดเจนยิ่งขึ้น เราจะเริ่มด้วยตัวอักษร:

"👩‍👩‍👧‍👦".characters.forEach { print($0) }
👩‍
👩‍
👧‍
👦

ตกลง. นั่นคือสิ่งที่เราคาดหวัง แต่มันเป็นเรื่องโกหก มาดูกันว่าตัวละครเหล่านั้นคืออะไร

"👩‍👩‍👧‍👦".characters.forEach { print(String($0).unicodeScalars.map{$0}) }
["\u{0001F469}", "\u{200D}"]
["\u{0001F469}", "\u{200D}"]
["\u{0001F467}", "\u{200D}"]
["\u{0001F466}"]

อ้า…มันเป็น["👩ZWJ", "👩ZWJ", "👧ZWJ", "👦"]เช่นนั้น ทำให้ทุกอย่างชัดเจนขึ้น 👩ไม่ได้เป็นสมาชิกของรายการนี้ (คือ "👩ZWJ") แต่👦เป็นสมาชิก

ปัญหาคือนั่นCharacterคือ "กลุ่ม grapheme" ซึ่งรวบรวมสิ่งต่าง ๆ เข้าด้วยกัน (เช่นการแนบ ZWJ) สิ่งที่คุณกำลังค้นหาจริงๆคือเซนต์คิตส์และเนวิส และทำงานได้อย่างที่คุณคาดหวัง:

"👩‍👩‍👧‍👦".unicodeScalars.contains("👩") // true
"👩‍👩‍👧‍👦".unicodeScalars.contains("\u{200D}") // true
"👩‍👩‍👧‍👦".unicodeScalars.contains("👧") // true
"👩‍👩‍👧‍👦".unicodeScalars.contains("👦") // true

และแน่นอนว่าเราสามารถมองหาตัวละครที่แท้จริงที่อยู่ในนั้น:

"👩‍👩‍👧‍👦".characters.contains("👩\u{200D}") // true

(นี่เป็นการทำซ้ำคะแนนของ Ben Leggiero อย่างมากฉันโพสต์สิ่งนี้ก่อนที่จะสังเกตเห็นว่าเขาตอบตอบออกไปในกรณีที่ทุกคนชัดเจนยิ่งขึ้น)


Wth จะZWJยืนหยัดเพื่อ?
LinusGeffarth

2
Zero Width Joiner
Rob Napier

@RobNapier ใน Swift 4 Stringถูกกล่าวหาว่าเปลี่ยนกลับเป็นประเภทการรวบรวม นั่นมีผลกระทบต่อคำตอบของคุณหรือไม่?
Ben Leggiero

ไม่นั่นเพิ่งเปลี่ยนสิ่งต่าง ๆ เช่นการห้อย มันไม่ได้เปลี่ยนวิธีการทำงานของตัวละคร
Rob Napier

75

ดูเหมือนว่าสวิฟต์จะพิจารณาว่าเป็นZWJกลุ่มกราฟกราฟิกแบบขยายพร้อมกับตัวละครที่อยู่ด้านหน้ามันทันที เราสามารถเห็นสิ่งนี้เมื่อทำแผนที่อาเรย์ของตัวละครไปที่unicodeScalars:

Array(manual.characters).map { $0.description.unicodeScalars }

นี่พิมพ์จาก LLDB ต่อไปนี้:

4 elements
  ▿ 0 : StringUnicodeScalarView("👩‍")
    - 0 : "\u{0001F469}"
    - 1 : "\u{200D}"1 : StringUnicodeScalarView("👩‍")
    - 0 : "\u{0001F469}"
    - 1 : "\u{200D}"2 : StringUnicodeScalarView("👧‍")
    - 0 : "\u{0001F467}"
    - 1 : "\u{200D}"3 : StringUnicodeScalarView("👦")
    - 0 : "\u{0001F466}"

นอกจากนี้.containsกลุ่มกราฟจะขยายกลุ่มเป็นอักขระเดียว ยกตัวอย่างเช่นการใช้ตัวอักษรฮันกึล, และ(ซึ่งรวมกันเพื่อให้คำเกาหลีสำหรับ "หนึ่ง": 한):

"\u{1112}\u{1161}\u{11AB}".contains("\u{1112}") // false

ไม่สามารถหาได้เพราะ codepoints สามตัวนั้นถูกจัดกลุ่มเป็นหนึ่งคลัสเตอร์ซึ่งทำหน้าที่เป็นอักขระเดียว ในทำนองเดียวกัน\u{1F469}\u{200D}( WOMAN ZWJ) คือหนึ่งคลัสเตอร์ซึ่งทำหน้าที่เป็นตัวละครตัวหนึ่ง


19

คำตอบอื่น ๆ พูดถึงสิ่งที่ Swift ทำ แต่ไม่ได้ลงรายละเอียดมากนักว่าทำไม

คุณคาดหวัง“ Å” ให้เท่ากับ“ Å” หรือไม่ ฉันคาดหวังว่าคุณจะ

หนึ่งในนั้นคือตัวอักษรที่มี combiner อีกตัวหนึ่งเป็นตัวอักษรที่ประกอบขึ้นเป็นหนึ่งเดียว คุณสามารถเพิ่มผู้ผสมที่แตกต่างกันให้กับตัวละครพื้นฐานและมนุษย์ยังคงคิดว่ามันเป็นตัวละครเดียว เพื่อจัดการกับความคลาดเคลื่อนประเภทนี้แนวคิดของกราฟได้ถูกสร้างขึ้นเพื่อแสดงถึงสิ่งที่มนุษย์จะพิจารณาถึงตัวละครโดยไม่คำนึงถึง codepoints ที่ใช้

ตอนนี้บริการส่งข้อความได้รับการรวมตัวละครเข้าไปในอีโมจิกราฟิกสำหรับปี→:) 🙂ดังนั้นอิโมจิต่างๆจึงถูกเพิ่มเข้าไปใน Unicode
บริการเหล่านี้เริ่มรวมอิโมจิเข้าด้วยกันเป็นคอมโพสิตอิโมจิ
แน่นอนว่าไม่มีวิธีที่เหมาะสมในการเข้ารหัสชุดค่าผสมที่เป็นไปได้ทั้งหมดลงใน codepoint แต่ละอันดังนั้น Unicode Consortium จึงตัดสินใจที่จะขยายแนวคิดของกราฟิกเพื่อล้อมตัวอักษรผสมเหล่านี้

สิ่งที่ทำให้เกิดความเดือดร้อนนี้"👩‍👩‍👧‍👦"ควรได้รับการพิจารณาว่าเป็น "กลุ่มกราฟ" เดียวหากคุณพยายามทำงานกับมันในระดับกราฟแกรมตามที่ Swift ดำเนินการตามค่าเริ่มต้น

หากคุณต้องการตรวจสอบว่ามันมี"👦"ส่วนหนึ่งของนั้นคุณควรลงไปในระดับที่ต่ำกว่า


ฉันไม่รู้จัก Swift syntax ดังนั้นนี่คือ Perl 6 ซึ่งมีระดับการสนับสนุน Unicode ในระดับใกล้เคียงกัน
(Perl 6 รองรับ Unicode รุ่น 9 ดังนั้นอาจมีความคลาดเคลื่อน)

say "\c[family: woman woman girl boy]" eq "👩‍👩‍👧‍👦"; # True

# .contains is a Str method only, in Perl 6
say "👩‍👩‍👧‍👦".contains("👩‍👩‍👧‍👦")    # True
say "👩‍👩‍👧‍👦".contains("👦");        # False
say "👩‍👩‍👧‍👦".contains("\x[200D]");  # False

# comb with no arguments splits a Str into graphemes
my @graphemes = "👩‍👩‍👧‍👦".comb;
say @graphemes.elems;                # 1

ลงไปอีกระดับ

# look at it as a list of NFC codepoints
my @components := "👩‍👩‍👧‍👦".NFC;
say @components.elems;                     # 7

say @components.grep("👦".ord).Bool;       # True
say @components.grep("\x[200D]".ord).Bool; # True
say @components.grep(0x200D).Bool;         # True

การลงมาที่ระดับนี้อาจทำให้บางสิ่งยากขึ้น

my @match = "👩‍👩‍👧‍👦".ords;
my $l = @match.elems;
say @components.rotor( $l => 1-$l ).grep(@match).Bool; # True

ฉันคิดว่า.containsใน Swift ทำให้ง่ายขึ้น แต่นั่นไม่ได้หมายความว่าไม่มีสิ่งอื่นใดที่ยากขึ้น

การทำงานที่ระดับนี้จะทำให้ง่ายขึ้นมากในการแยกสตริงโดยไม่ตั้งใจซึ่งอยู่ตรงกลางของอักขระผสม


สิ่งที่คุณถามโดยไม่ตั้งใจคือเหตุใดการแสดงระดับสูงกว่านี้จึงไม่ทำงานเหมือนการแสดงระดับล่าง คำตอบคือแน่นอนมันไม่ควรจะ

หากคุณถามตัวเองว่าทำไมเรื่องนี้ถึงซับซ้อนมาก ” คำตอบก็คือ“ มนุษย์


4
คุณทำฉันในบรรทัดตัวอย่างสุดท้ายของคุณ; ทำอะไรrotorและgrepทำที่นี่ แล้วอะไร1-$lล่ะ
Ben Leggiero

4
คำว่า "grapheme" มีอายุอย่างน้อย 50 ปี Unicode แนะนำให้รู้จักกับมาตรฐานเพราะพวกเขาใช้คำว่า "character" เพื่อหมายถึงบางสิ่งที่แตกต่างจากสิ่งที่คนทั่วไปคิดว่าเป็นตัวละคร ฉันสามารถอ่านสิ่งที่คุณเขียนว่าสอดคล้องกับสิ่งนั้น แต่สงสัยว่าคนอื่นอาจได้รับการแสดงผลที่ไม่ถูกต้องดังนั้นความคิดเห็น (หวังว่าชัดเจน) นี้
raiph

2
@BenLeggiero rotorแรก รหัสอัตราผลตอบแทนsay (1,2,3,4,5,6).rotor(3) ((1 2 3) (4 5 6))นั่นคือรายการของความยาวแต่ละ3รายการ say (1,2,3,4,5,6).rotor(3=>-2)ให้ผลเหมือนกันยกเว้นรายการย่อยที่สองเริ่มต้นด้วย2แทนที่จะเป็น4รายการที่สามด้วย3และอื่น ๆ ตาม((1 2 3) (2 3 4) (3 4 5) (4 5 6))ลำดับ หาก@matchมี"👩‍👩‍👧‍👦".ordsรหัสของ @ Brad อยู่แล้วจะสร้างเพียงหนึ่งรายการย่อยดังนั้น=>1-$lบิตจะไม่เกี่ยวข้อง (ไม่ได้ใช้) มันเป็นเรื่องที่เกี่ยวข้องเฉพาะในกรณีที่สั้นกว่า@match @components
raiph

1
grepพยายามจับคู่แต่ละองค์ประกอบใน invocant ของมัน (ในกรณีนี้คือรายการของรายการย่อย@components) มันพยายามจับคู่แต่ละองค์ประกอบกับอาร์กิวเมนต์ตัวจับคู่ (ในกรณีนี้@match) .Boolผลตอบแทนแล้วTrueIFF grepผลิตอย่างน้อยหนึ่งในการแข่งขัน
raiph

18

อัปเดต Swift 4.0

String ได้รับจำนวนมากของการแก้ไขในสวิฟท์ 4 ปรับปรุงเป็นเอกสารในSE-0163 อีโมจิสองตัวใช้สำหรับการสาธิตนี้ซึ่งแสดงถึงโครงสร้างที่แตกต่างกันสองแบบ ทั้งสองรวมกันกับลำดับของอีโมจิ

👍🏽เป็นการรวมกันของสองอีโมจิ👍และ🏽

👩‍👩‍👧‍👦เป็นการรวมกันของสี่อิโมจิที่มีการเชื่อมต่อช่างเชื่อมความกว้างเป็นศูนย์ รูปแบบคือ👩‍joiner👩‍joiner👧‍joiner👦

1. การนับ

ใน Swift 4.0 emoji จะถูกนับเป็นกลุ่มกราฟ อิโมจิทุกตัวจะถูกนับเป็น 1 countคุณสมบัตินี้ยังมีให้บริการโดยตรงสำหรับสตริง คุณสามารถเรียกมันได้แบบนี้โดยตรง

"👍🏽".count  // 1. Not available on swift 3
"👩‍👩‍👧‍👦".count  // 1. Not available on swift 3

แถวอักขระของสตริงยังนับเป็นกลุ่มอักษรในสวิฟท์ 4.0 เพื่อให้ทั้งสองของรหัสต่อไปนี้ 1. พิมพ์ทั้งสองอีโมจิเป็นตัวอย่างของลำดับอีโมจิที่หลายอีโมจิจะถูกรวมเข้าด้วยกันโดยมีหรือไม่มีตัวเชื่อมความกว้างศูนย์\u{200d}ระหว่างพวกเขา ใน swift 3.0 อาร์เรย์อักขระของสตริงดังกล่าวจะแยกอีโมจิแต่ละตัวออกมาและส่งผลให้อาร์เรย์มีองค์ประกอบหลายอย่าง (อีโมจิ) ผู้เข้าร่วมถูกละเว้นในกระบวนการนี้ อย่างไรก็ตามใน Swift 4.0 อาร์เรย์อักขระจะเห็นอิโมจิทั้งหมดเป็นชิ้นเดียว อีโมจิใด ๆ ก็จะเป็น 1 เสมอ

"👍🏽".characters.count  // 1. In swift 3, this prints 2
"👩‍👩‍👧‍👦".characters.count  // 1. In swift 3, this prints 4

unicodeScalars ยังคงไม่เปลี่ยนแปลงใน Swift 4 มันมีอักขระ Unicode ที่ไม่ซ้ำกันในสตริงที่กำหนด

"👍🏽".unicodeScalars.count  // 2. Combination of two emoji
"👩‍👩‍👧‍👦".unicodeScalars.count  // 7. Combination of four emoji with joiner between them

2. ประกอบด้วย

ใน Swift 4.0 containsเมธอดจะไม่สนใจตัวเชื่อมความกว้างศูนย์ในอิโมจิ ดังนั้นจึงส่งคืนจริงสำหรับองค์ประกอบอิโมจิทั้งสี่ของ"👩‍👩‍👧‍👦"และคืนเท็จถ้าคุณตรวจสอบผู้เข้าร่วม อย่างไรก็ตามใน Swift 3.0 ผู้เข้าร่วมจะไม่ถูกเพิกเฉยและถูกรวมเข้ากับอิโมจิที่อยู่ด้านหน้า ดังนั้นเมื่อคุณตรวจสอบว่า"👩‍👩‍👧‍👦"มีอิโมจิสามองค์ประกอบแรกผลลัพธ์จะเป็นเท็จหรือไม่

"👍🏽".contains("👍")       // true
"👍🏽".contains("🏽")        // true
"👩‍👩‍👧‍👦".contains("👩‍👩‍👧‍👦")       // true
"👩‍👩‍👧‍👦".contains("👩")       // true. In swift 3, this prints false
"👩‍👩‍👧‍👦".contains("\u{200D}") // false
"👩‍👩‍👧‍👦".contains("👧")       // true. In swift 3, this prints false
"👩‍👩‍👧‍👦".contains("👦")       // true

0

อิโมจิคล้ายกับมาตรฐานยูนิโคดที่ซับซ้อนอย่างหลอกลวง โทนสีผิว, เพศ, งาน, กลุ่มคน, ลำดับผู้เข้าร่วมที่มีความกว้างเป็นศูนย์, แฟล็ก (ยูนิโค้ด 2 ตัวอักษร) และภาวะแทรกซ้อนอื่น ๆ สามารถทำให้อีโมจิแยกวิเคราะห์ยุ่งเหยิง ต้นคริสต์มาส Slice of Pizza หรือ Pile of Poop สามารถแสดงด้วยจุดรหัส Unicode เดียว ไม่ต้องพูดถึงว่าเมื่อมีการเปิดตัวอีโมจิใหม่จะมีความล่าช้าระหว่างการสนับสนุน iOS และการปล่อยอีโมจิ และความจริงที่ว่า iOS เวอร์ชันต่าง ๆ สนับสนุนมาตรฐาน unicode ต่างกัน

TL; DR ฉันทำงานเกี่ยวกับคุณสมบัติเหล่านี้และเปิดห้องสมุดฉันเป็นผู้เขียนJKEmojiเพื่อช่วยวิเคราะห์สตริงด้วยอิโมจิ มันทำให้การแยกวิเคราะห์เป็นเรื่องง่ายเหมือน:

print("I love these emojis 👩‍👩‍👧‍👦💪🏾🧥👧🏿🌈".emojiCount)

5

มันทำเช่นนั้นโดยการรีเฟรชฐานข้อมูลโลคัลของอิโมจิที่รู้จักทั้งหมดเป็นประจำเป็นรุ่นล่าสุดของยูนิโค้ด ( 12.0เป็นล่าสุด) และอ้างอิงข้ามพวกเขาด้วยสิ่งที่ได้รับการยอมรับว่าเป็นอิโมจิที่ถูกต้องในระบบปฏิบัติการเวอร์ชั่น อักขระอีโมจิที่ไม่รู้จัก

บันทึก

คำตอบก่อนหน้านี้ถูกลบสำหรับการโฆษณาห้องสมุดของฉันโดยไม่ระบุว่าฉันเป็นผู้เขียนอย่างชัดเจน ฉันยอมรับสิ่งนี้อีกครั้ง


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