একটি স্ট্রিংয়ে অক্ষরের সংখ্যা কীভাবে পাবেন?


145

আমি কীভাবে স্ট্রিংয়ের অক্ষরের সংখ্যা পেতে পারি?

উদাহরণস্বরূপ, আমার যদি স্ট্রিং থাকে "hello"তবে পদ্ধতিটি ফিরে আসা উচিত 5। আমি যে দেখেছি len(str)আয় বাইটের সংখ্যা এবং তাই অক্ষরের সংখ্যা len("£")আয় 2 পরিবর্তে 1 কারণ £ হল UTF-8 দুটি বাইট সাথে এনকোডেড হয়েছে।


2
এটি 5 ফেরত দেয় । ফাইল এনকোডিংটি ইউটিএফ -8 থাকাকালীন এটি না করে।
মোশে রেভাঃ

7
হ্যাঁ এটি এই ক্ষেত্রে করে তবে আমি এটি আরবি-র মতো অন্যান্য ইউটিএফ -8 অক্ষরের জন্য সাধারণ করতে চাই, যা 1 বাইটে অনুবাদ করে না।
আম্মার

উত্তর:


177

আপনি RuneCountInStringutf8 প্যাকেজ থেকে চেষ্টা করতে পারেন ।

পি তে রুনসের সংখ্যা প্রদান করে

এই স্ক্রিপ্টে যেমন চিত্রিত হয়েছে : "ওয়ার্ল্ড" এর দৈর্ঘ্য 6 হতে পারে (যখন চীনা ভাষায় লেখা হয়: "世界") তবে এর রুন গণনা 2:

package main

import "fmt"
import "unicode/utf8"

func main() {
    fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界"))
}

Phrozen যোগ মন্তব্য :

আসলে আপনি len()কেবল castালাই টাইপ করে রুনসকে ওভার করতে পারেন ।
len([]rune("世界"))মুদ্রণ করবে 2। Go 1.3 এ লিজ এ।


এবং সিএল 108985 (মে 2018, Go 1.11 এর জন্য) len([]rune(string))দিয়ে এখন অনুকূলিত হয়েছে। (সংশোধন ইস্যু 24923 )

সংকলক len([]rune(string))স্বয়ংক্রিয়ভাবে প্যাটার্ন সনাক্ত করে এবং এটি r: = রেঞ্জের কলের জন্য প্রতিস্থাপন করে।

স্ট্রিংয়ে রানগুলি গণনা করতে একটি নতুন রানটাইম ফাংশন যুক্ত করে। প্যাটার্নটি সনাক্ত করতে সংকলকটি সংশোধন করে len([]rune(string)) এবং নতুন রুনি গণনা রানটাইম ফাংশনের সাথে এটি প্রতিস্থাপন করে।

RuneCount/lenruneslice/ASCII                  27.8ns ± 2%  14.5ns ± 3%  -47.70%  (p=0.000 n=10+10)
RuneCount/lenruneslice/Japanese                126ns ± 2%    60ns ± 2%  -52.03%  (p=0.000 n=10+10)
RuneCount/lenruneslice/MixedLength             104ns ± 2%    50ns ± 1%  -51.71%  (p=0.000 n=10+9)

স্টিফান Steiger ব্লগ পোস্টে "থেকে পয়েন্ট যান পাঠ্য নিয়মমাফিককরণ "

একটি চরিত্র কি?

স্ট্রিং ব্লগ পোস্টে যেমন উল্লেখ করা হয়েছিল , অক্ষরগুলি একাধিক রুনে বিস্তৃত হতে পারে
উদাহরণস্বরূপ, একটি ' e' এবং '◌́◌́' (তীব্র "\ u0301") একত্রিত হয়ে 'é' (এনএফডিতে "") গঠন করতে পারে e\u0301এই দুটি রুন একসাথে একটি চরিত্র

কোনও অক্ষরের সংজ্ঞা প্রয়োগের উপর নির্ভর করে পরিবর্তিত হতে পারে।
জন্য নিয়মমাফিককরণ আমরা এটা যেমন সংজ্ঞায়িত হবে:

  • রুনসের ক্রম যা একটি স্টার্টার দিয়ে শুরু হয়,
  • একটি রুন যা অন্য কোনও রুনের সাথে পিছনের দিকে সংশোধন বা সংযুক্ত করে না,
  • এর পরে সম্ভবত অ-স্টার্টারদের খালি সিকোয়েন্সটি হয়, তা হয় যে রানগুলি (সাধারণত উচ্চারণ)।

নরমালাইজেশন অ্যালগরিদম সময়ে একটি অক্ষর প্রক্রিয়া করে।

সেই প্যাকেজটি এবং এর Iterধরণটি ব্যবহার করে "অক্ষর" এর আসল সংখ্যাটি হ'ল:

package main

import "fmt"
import "golang.org/x/text/unicode/norm"

func main() {
    var ia norm.Iter
    ia.InitString(norm.NFKD, "école")
    nc := 0
    for !ia.Done() {
        nc = nc + 1
        ia.Next()
    }
    fmt.Printf("Number of chars: %d\n", nc)
}

এখানে, এটি ইউনিকোড নরমালাইজেশন ফর্ম NFKD "সামঞ্জস্যতা পচন" ব্যবহার করে


অলিভার এর উত্তর পয়েন্ট ইউনিকোড টেক্সট সেগমেন্টেশন ব্যবহারকারী-অনুভূত অক্ষর, শব্দ, এবং বাক্য: নির্ভরযোগ্যভাবে নির্দিষ্ট উল্লেখযোগ্য টেক্সট উপাদানের মধ্যে ডিফল্ট গণ্ডি নির্ণয় করার একমাত্র উপায় হিসাবে।

তার জন্য আপনার রিভো / ইউনিসেগের মতো একটি বাহ্যিক গ্রন্থাগার দরকার যা ইউনিকোড পাঠ্য বিভাগকে দেয় ।

এটি প্রকৃতপক্ষে " গ্রাফি ক্লাস্টার " গণনা করবে , যেখানে একাধিক কোড পয়েন্ট এক ব্যবহারকারী-অনুভূত অক্ষরে একত্রিত হতে পারে।

package uniseg

import (
    "fmt"

    "github.com/rivo/uniseg"
)

func main() {
    gr := uniseg.NewGraphemes("👍🏼!")
    for gr.Next() {
        fmt.Printf("%x ", gr.Runes())
    }
    // Output: [1f44d 1f3fc] [21]
}

দুটি গ্রাফিম, যদিও তিনটি রান রয়েছে (ইউনিকোড কোড পয়েন্ট)।

আপনি অন্যান্য উদাহরণ দেখতে পারেন " বারেই স্ট্রিং নিপূণভাবে কিভাবে তাদের বিপরীত কিভাবে? "

🦰‍🦰 একা এক গ্রাফিয়াম, তবে, ইউনিকোড থেকে কোড পয়েন্ট রূপান্তরকারী , 4 রান:


4
আপনি এই স্ট্রিং রিভারশন ফাংশনে কর্ম এটা দেখতে পারেন stackoverflow.com/a/1758098/6309
VonC

5
এটি আপনাকে রুনের সংখ্যা বলবে, গ্লিফের সংখ্যা নয়। অনেকগুলি গ্লাইফ একাধিক রান থেকে তৈরি।
স্টিফেন ওয়েনবার্গ

5
প্রকৃতপক্ষে আপনি কেবল কাস্টিং টাইপ করে রানগুলি ওপেন করতে পারেন ... লেন ([] রুনে ("世界")) 2 মুদ্রণ করবে 2. গো 1.3 এর লিটগুলিতে কত দিন হয়ে গেছে তা জানুন।
ফিরোজেন

3
@ ভনসি: প্রকৃতপক্ষে, একটি চরিত্র (গ্লাইফের জন্য কথোপকথন ভাষার শব্দ) - মাঝে মাঝে - কয়েক রুন বিস্তৃত করতে পারে, সুতরাং এই উত্তরটি হ'ল যথাযথ টেকনিক্যাল শব্দটি ব্যবহার করতে হবে, র্রং। আপনার যা দরকার তা হ'ল গ্রাফেম / গ্রাফেমক্লাস্টার গণনা, রান রেকর্ড নয়। উদাহরণস্বরূপ, একটি 'ই' এবং '◌́' (তীব্র "\ u0301") একত্রিত হয়ে 'é' (এনএফডিতে "e \ u0301") গঠন করতে পারে। কিন্তু একটি মানুষের সম্মতি (সঠিকভাবে) সম্মান & eacute; ওয়ান চরিত্র হিসাবে .. স্পষ্টতই এটি তেলেগুতে আলাদা করে। তবে আপনার ব্যবহৃত কীবোর্ড / লোকালের উপর নির্ভর করে ফ্রেঞ্চও। blog.golang.org/normalization
স্টিফান স্টেইগার

1
@ জাস্টিন জনসন একমত হয়েছেন অলিভার'র উত্তরটি আমি সম্পাদনা করেছি, যা আমি পূর্বে উন্নত করেছিলাম।
ভোনসি

43

কোনও প্যাকেজ ছাড়াই রুনের গণনা পাওয়ার উপায় আছে যা স্ট্রিংকে [] রুনে রূপান্তর করে len([]rune(YOUR_STRING)):

package main

import "fmt"

func main() {
    russian := "Спутник и погром"
    english := "Sputnik & pogrom"

    fmt.Println("count of bytes:",
        len(russian),
        len(english))

    fmt.Println("count of runes:",
        len([]rune(russian)),
        len([]rune(english)))

}

বাইট সংখ্যা 30 16

রানসের গণনা 16 16


5

"চরিত্র" কী তা আপনার সংজ্ঞার উপর অনেক কিছু নির্ভর করে। যদি "রুনে একটি চরিত্রের সমান হয়" আপনার কাজের জন্য ঠিক থাকে (সাধারণত এটি হয় না) তবে ভনসির উত্তর আপনার জন্য উপযুক্ত। অন্যথায়, সম্ভবত এটি লক্ষ করা উচিত, এমন কয়েকটি পরিস্থিতি রয়েছে যেখানে ইউনিকোড স্ট্রিংয়ে রানসের সংখ্যা একটি আকর্ষণীয় মান। এবং এমনকি এই পরিস্থিতিতেও ইউটিএফ -8 ডিকোড প্রচেষ্টা দ্বিগুণ না করার জন্য রুনগুলি প্রক্রিয়াজাতকরণের সাথে স্ট্রিং "ট্র্যাভারিং" করার সময় গণনা অনুমান করা ভাল।


আপনি কখন কোনও চরিত্রকে কোনও চরিত্র হিসাবে দেখবেন না ? গো স্পেক একটি রুনিকে ইউনিকোড কোডপয়েন্ট হিসাবে সংজ্ঞায়িত করেছে: golang.org/ref/spec#Rune_literals
থমাস ক্যাপলার

এছাড়াও, ডিকোড প্রচেষ্টা দ্বিগুণ হওয়া এড়াতে, আমি কেবল একটি [] রুনে (স্ট্রিং) করি, তার উপর কাজ করি, তারপরে আমি যখন হয়ে যাই তখন স্ট্রিংয়ে ফিরে রূপান্তর করি। আমি মনে করি স্ট্রিংকে ট্র্যাভার করার সময় কোড পয়েন্টগুলি ট্র্যাক করার চেয়ে সহজ।
থমাস ক্যাপলার

4
@ থমাসক্যাপলার: কখন? ঠিক আছে, যখন রুন কোনও চরিত্র নয়, যা সাধারণত হয় না। কেবলমাত্র কিছু রানগুলি অক্ষরের সমান, সমস্ত নয়। "রুনে == অক্ষর" ধরে নেওয়া কেবল ইউনিকোডের অক্ষরের সাবসেটের জন্য বৈধ। উদাহরণ: en.wikedia.org/wiki/…
zzzz

@ThomasKappler: কিন্তু আপনি তা যে ভাবে তাকান, তারপর যেমন জাভার Stringএর .length()পদ্ধতি অক্ষরের সংখ্যা ফেরত দেয় না পারেন। আমরাও না কোকো এর NSString'র -lengthপদ্ধতি। এগুলি কেবল ইউটিএফ -16 সংস্থার সংখ্যা ফেরত দেয়। তবে কোডপয়েন্টের আসল সংখ্যাটি খুব কমই ব্যবহৃত হয়, কারণ এটি গণনা করতে লিনিয়ার সময় লাগে।
newacct

5

আপনার যদি গ্রাফিম ক্লাস্টারগুলিকে অ্যাকাউন্টে নেওয়া দরকার তবে রেজিএক্সপ্যাক বা ইউনিকোড মডিউলটি ব্যবহার করুন। গ্রাফি ক্লাস্টারের দৈর্ঘ্য সীমাহীন হওয়ায় ভ্যালাইডাইনের জন্য কোড পয়েন্ট (রুনস) বা বাইট সংখ্যাও গণনা করা প্রয়োজন। যদি আপনি চূড়ান্ত দীর্ঘ সিকোয়েন্সগুলি মুছে ফেলতে চান তবে সিকোয়েন্সগুলি স্ট্রিম-নিরাপদ পাঠ্য বিন্যাসের সাথে সামঞ্জস্য করে কিনা তা পরীক্ষা করে দেখুন

package main

import (
    "regexp"
    "unicode"
    "strings"
)

func main() {

    str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308"
    str2 := "a" + strings.Repeat("\u0308", 1000)

    println(4 == GraphemeCountInString(str))
    println(4 == GraphemeCountInString2(str))

    println(1 == GraphemeCountInString(str2))
    println(1 == GraphemeCountInString2(str2))

    println(true == IsStreamSafeString(str))
    println(false == IsStreamSafeString(str2))
}


func GraphemeCountInString(str string) int {
    re := regexp.MustCompile("\\PM\\pM*|.")
    return len(re.FindAllString(str, -1))
}

func GraphemeCountInString2(str string) int {

    length := 0
    checked := false
    index := 0

    for _, c := range str {

        if !unicode.Is(unicode.M, c) {
            length++

            if checked == false {
                checked = true
            }

        } else if checked == false {
            length++
        }

        index++
    }

    return length
}

func IsStreamSafeString(str string) bool {
    re := regexp.MustCompile("\\PM\\pM{30,}") 
    return !re.MatchString(str) 
}

এর জন্য ধন্যবাদ. আমি আপনার কোডটি চেষ্টা করেছি এবং এটি এর মতো কয়েকটি ইমোজি গ্রাফিমের জন্য কাজ করে না: 🖖🏿🇸🇴 🖖🏿🇸🇴 সেগুলি সঠিকভাবে গণনা করার জন্য কোনও চিন্তাভাবনা?
বজর্ন রোচে

সংকলিত regexp varফাংশনের বাইরে হিসাবে উত্তোলন করা উচিত ।
ডোলম্যান

5

স্ট্রিংয়ের দৈর্ঘ্য পাওয়ার বিভিন্ন উপায় রয়েছে:

package main

import (
    "bytes"
    "fmt"
    "strings"
    "unicode/utf8"
)

func main() {
    b := "这是个测试"
    len1 := len([]rune(b))
    len2 := bytes.Count([]byte(b), nil) -1
    len3 := strings.Count(b, "") - 1
    len4 := utf8.RuneCountInString(b)
    fmt.Println(len1)
    fmt.Println(len2)
    fmt.Println(len3)
    fmt.Println(len4)

}


3

আমার উল্লেখ করা উচিত যে এখন পর্যন্ত প্রদত্ত জবাবগুলির কোনওটিই আপনার প্রত্যাশার মতো অক্ষরের সংখ্যা দেয় না, বিশেষত যখন আপনি ইমোজিগুলি নিয়ে কাজ করছেন (তবে থাই, কোরিয়ান বা আরবি জাতীয় কিছু ভাষাও)। ভনসির পরামর্শগুলি নিম্নলিখিত আউটপুট দেবে:

fmt.Println(utf8.RuneCountInString("🏳️‍🌈🇩🇪")) // Outputs "6".
fmt.Println(len([]rune("🏳️‍🌈🇩🇪"))) // Outputs "6".

কারণ এই পদ্ধতিগুলি কেবল ইউনিকোড কোড পয়েন্ট গণনা করে। অনেকগুলি অক্ষর রয়েছে যা একাধিক কোড পয়েন্ট সমন্বয়ে গঠিত হতে পারে।

সাধারণকরণ প্যাকেজটি ব্যবহারের জন্য একই :

var ia norm.Iter
ia.InitString(norm.NFKD, "🏳️‍🌈🇩🇪")
nc := 0
for !ia.Done() {
    nc = nc + 1
    ia.Next()
}
fmt.Println(nc) // Outputs "6".

সাধারণীকরণ প্রকৃতপক্ষে গণনা অক্ষরের সমান নয় এবং অনেকগুলি অক্ষরকে ওয়ান-কোড-পয়েন্ট সমতুল্য করা যায় না।

মাসাকিয়েলেস্টিকের উত্তর নিকটে আসে তবে কেবলমাত্র সংশোধককে পরিচালনা করে (রেইনবো পতাকাটিতে একটি সংশোধক থাকে যা এইভাবে তার নিজস্ব কোড পয়েন্ট হিসাবে গণ্য হয় না):

fmt.Println(GraphemeCountInString("🏳️‍🌈🇩🇪"))  // Outputs "5".
fmt.Println(GraphemeCountInString2("🏳️‍🌈🇩🇪")) // Outputs "5".

ইউনিকোড স্ট্রিংগুলিকে (ব্যবহারকারীর উপলব্ধি) অক্ষরগুলিতে বিভক্ত করার সঠিক উপায়, যেমন গ্রাফি ক্লাস্টারগুলিকে ইউনিকোড স্ট্যান্ডার্ড অ্যাঙ্কেক্স # 29 এ সংজ্ঞায়িত করা হয়েছে । বিধিগুলি অনুচ্ছেদ 3.1.1 এ পাওয়া যাবে । Github.com/rivo/uniseg প্যাকেজ কার্যকরী এই নিয়ম আপনি একটি স্ট্রিং অক্ষরের সঠিক সংখ্যা নির্ধারণ করতে পারেন যাতে:

fmt.Println(uniseg.GraphemeClusterCount("🏳️‍🌈🇩🇪")) // Outputs "2".

0

আমি কিছুটা দ্রুত নরমালাইজেশন করার চেষ্টা করেছি:

    en, _ = glyphSmart(data)

    func glyphSmart(text string) (int, int) {
        gc := 0
        dummy := 0
        for ind, _ := range text {
            gc++
            dummy = ind
        }
        dummy = 0
        return gc, dummy
    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.