Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
家好,我是站長 polarisxu。
今天要聊的內容應該可以當做一道面試題,你可以先想想該怎么實現。
統計字數是一個很常見的需求,很多人印象最深的應該是微博早些時候限制 140 字,而且邊輸入會邊統計剩余字數。現在很多社區文章也會有字數統計的功能,而且可以依據字數來預估閱讀時間。比如 Go語言中文網就有這樣的功能。
下手之前先分析下這個需求。從我個人經驗看,在實際面試中,針對一個面試題,你的分析過程,循序漸進的解決方案,可以很好的展示你的思考過程。正所謂分析問題、解決問題。這會給你加分的。
我們采用類似詞法分析的思路分析這個需求。
一篇文章通常包含如下元素,我們也稱之為 token:
其中普通文字通常會分為歐美和中日韓(CJK),因為 CJK 屬于表意文字,和歐美字母的文字差異很大。同時這里還涉及到編碼的問題。本文假設使用 UTF-8 編碼。
對于標點符號,中文標點和英文標點也會很不一樣。
此外還有全角和半角的問題。
根據以上分析,對于該需求作如下假定:
本文的解決方案針對以上的假定進行。
先看最簡單的。
根據以上分析,如果文章只包含普通文本且是英文,也就是說,每個字(單詞)根據空格分隔,統計是最簡單的。
func TotalWords(s string) int {
n := 0
inWord := false
for _, r := range s {
wasInWord := inWord
inWord = !unicode.IsSpace(r)
if inWord && !wasInWord {
n++
}
}
return n
}
還有一種更簡單的方式:
len(strings.Fields(s))
不過看 strings.Fields 的實現,性能會不如第一種方式。
回顧上面的需求分析,會發現這個實現是有 Bug 的。比如下面的例子:
s1 := "Hello,playground"
s2 := "Hello, playground"
用上面的實現,s1 的字數是 1,s2 的字數是 2。它們都忽略了標點符號。而且因為寫法的多樣性(不規范統一),導致計算字數會有誤差。所以我們需要對寫法進行規范。
其實和寫代碼要有規范一樣,文章也是有規范的。比如出版社對于一本書的排版會有明確的規定。為了讓我們的文章看起來更舒服,也應該遵循一定的規范。
這里推薦一個 GitHub 上的排版指南:《中文文案排版指北》,它的宗旨,統一中文文案、排版的相關用法,降低團隊成員之間的溝通成本,增強網站氣質。這個規范開頭關于空格的一段話很有意思:
有研究顯示,打字的時候不喜歡在中文和英文之間加空格的人,感情路都走得很辛苦,有七成的比例會在 34 歲的時候跟自己不愛的人結婚,而其余三成的人最后只能把遺產留給自己的貓。畢竟愛情跟書寫都需要適時地留白。
建議大家可以看看這個指北,一些知名的網站就是按照這個做的。
因為 GCTT 的排版在這個規范做,但人為約束不是最好的方法,所以我開發了一個 Go 工具:https://github.com/studygolang/autocorrect,用于自動給中英文之間加入合理的空格并糾正專用名詞大小寫。
所以為了讓字數統計更準確,我們假定文章是按一定的規范書寫的。比如上面的例子,規范的寫法是 s2 :="Hello, playground"。不過這里標點不算作字數。
剛去微博上試了一下,發現微博的字數計算方式有點詭異,竟然是 9 個字。
測試一下發現,它直接把兩個英文字母算作一個字(兩個字節算一個字)。而漢字是正常的。大家可以想想微博是怎么實現的。
中文不像英文,單詞之間沒有空格分隔,因此開始的那兩種方式不適合。
如果是純中文,我們怎么計算字數呢?
在 Go 語言中,字符串使用 UTF-8 編碼,一個字符用 rune 表示。因此在標準庫中查找相關計算方法。
func RuneCountInString(s string) (n int)
這個方法能計算字符串包含的 rune(字符)數,對于純中文,就是漢字數。
str := "你好世界"
fmt.Println(utf8.RuneCountInString(str))
以上代碼輸出 4。
然而,因為很多時候文章會中英文混合,因此我們先采用上面的純英文的處理方式,即:strings.Fields(),將文章用空格分隔,然后處理每一部分。
func TotalWords(s string) int {
wordCount := 0
plainWords := strings.Fields(s)
for _, word := range plainWords {
runeCount := utf8.RuneCountInString(word)
if len(word) == runeCount {
wordCount++
} else {
wordCount += runeCount
}
}
return wordCount
}
增加如下的測試用例:
func TestTotalWords(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
{"en1", "hello,playground", 2},
{"en2", "hello, playground", 2},
{"cn1", "你好世界", 4},
{"encn1", "Hello你好世界", 5},
{"encn2", "Hello 你好世界", 5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := wordscount.TotalWords(tt.input); got != tt.want {
t.Errorf("TotalWords() = %v, want %v", got, tt.want)
}
})
}
}
發現 en1 和 encn1 測試不通過,因為沒有按照上面說的規范書寫。因此我們通過程序增加必要的空格。
// AutoSpace 自動給中英文之間加上空格
func AutoSpace(str string) string {
out := ""
for _, r := range str {
out = addSpaceAtBoundary(out, r)
}
return out
}
func addSpaceAtBoundary(prefix string, nextChar rune) string {
if len(prefix) == 0 {
return string(nextChar)
}
r, size := utf8.DecodeLastRuneInString(prefix)
if isLatin(size) != isLatin(utf8.RuneLen(nextChar)) &&
isAllowSpace(nextChar) && isAllowSpace(r) {
return prefix + " " + string(nextChar)
}
return prefix + string(nextChar)
}
func isLatin(size int) bool {
return size == 1
}
func isAllowSpace(r rune) bool {
return !unicode.IsSpace(r) && !unicode.IsPunct(r)
}
這樣可以在 TotalWords 函數開頭增加 AutoSpace 進行規范化。這時結果就正常了。
以上例子標點沒計算在內,而且如果英文和中文標點混合在一起,情況又復雜了。
為了更好地實現開始的需求分析,重構以上代碼,設計如下的結構:
type Counter struct {
Total int // 總字數 = Words + Puncts
Words int // 只包含字符數
Puncts int // 標點數
Links int // 鏈接數
Pics int // 圖片數
CodeLines int // 代碼行數
}
同時將 TotalWords 重構為 Counter 的 Stat 方法,同時記錄標點數:
func (wc *Counter) Stat(str string) {
wc.Links = len(rxStrict.FindAllString(str, -1))
wc.Pics = len(imgReg.FindAllString(str, -1))
// 剔除 HTML
str = StripHTML(str)
str = AutoSpace(str)
// 普通的鏈接去除(非 HTML 標簽鏈接)
str = rxStrict.ReplaceAllString(str, " ")
plainWords := strings.Fields(str)
for _, plainWord := range plainWords {
words := strings.FieldsFunc(plainWord, func(r rune) bool {
if unicode.IsPunct(r) {
wc.Puncts++
return true
}
return false
})
for _, word := range words {
runeCount := utf8.RuneCountInString(word)
if len(word) == runeCount {
wc.Words++
} else {
wc.Words += runeCount
}
}
}
wc.Total = wc.Words + wc.Puncts
}
var (
rxStrict = xurls.Strict()
imgReg = regexp.MustCompile(`<img [^>]*>`)
stripHTMLReplacer = strings.NewReplacer("\n", " ", "</p>", "\n", "<br>", "\n", "<br />", "\n")
)
// StripHTML accepts a string, strips out all HTML tags and returns it.
func StripHTML(s string) string {
// Shortcut strings with no tags in them
if !strings.ContainsAny(s, "<>") {
return s
}
s = stripHTMLReplacer.Replace(s)
// Walk through the string removing all tags
b := GetBuffer()
defer PutBuffer(b)
var inTag, isSpace, wasSpace bool
for _, r := range s {
if !inTag {
isSpace = false
}
switch {
case r == '<':
inTag = true
case r == '>':
inTag = false
case unicode.IsSpace(r):
isSpace = true
fallthrough
default:
if !inTag && (!isSpace || (isSpace && !wasSpace)) {
b.WriteRune(r)
}
}
wasSpace = isSpace
}
return b.String()
}
代碼過多的細節不討論。此外,關于文章內的代碼行數統計未實現(目前沒有想到特別好的方法,如果你有,歡迎交流)。
通過本文的分析發現,精準統計字數沒那么容易,這里涉及到很多的細節。
當然,實際應用中,字數不需要那么特別精準,而且對于非正常文字(比如鏈接、代碼)怎么處理,會有不同的約定。
本文涉及到的完整代碼放在 GitHub:https://github.com/polaris1119/wordscount。
常在很多網站編輯文本回答或發博客時,經常會遇到有最大或最小字數限制的問題,字數如果少了或多了,可能發不出去或沒有平臺獎勵,好不容易編輯好的回答,又要重新修改,如此反復,很浪費時間和精力,如果字數少還好,可以自己數,但是字數多了,不僅費時間,而且自己很容易數錯,唉,所以,達芬奇今天就同大家分享幾個可以快速得到字數多少的方法,望能幫助到需要的朋友。
方法一:通過登錄字數計數網站進行快速查詢。
打開網絡瀏覽器,然后在窗口網址輸入欄輸入以下任一網址,摁“enther”鍵進入網站 ,然后復制你所編輯的網頁回答或博客文章,然后粘貼進上述網址相應位置(見下圖),即可得到文本字數。
網址一:文本計數網(word counter):https://wordcounter.net/
如下圖所示,word counter是一個專業的文本計數網站,僅需把所復制文本,粘貼在下述框圖里,便自動計數,且不僅包含文本的字數,包括行數,段數以及相應的關鍵詞,及關鍵詞使用次數都可得到,通過word counter不僅可得到文本的詳細信息,同時通過對關鍵詞及相應的關鍵詞使用次數,可使文本編輯者優化及豐富自己所用“關鍵詞”,使文本遣詞造句更加合理豐富。
網址2:javascriptkit
http://www.javascriptkit.com/script/script2/countwords.shtml
如下圖所示,javascriptkit提供的文本計數服務,雖不如wordcounter那么豐富,但對于我們的計數需求完全可以勝任。
方法二:借助軟件,進行文本計數。
下載地址:https://wwc.lanzouw.com/i7OcY0b1nude,密碼:7mj3
使用方法:如下圖所示,安裝完成后,打開“notepad++”,然后粘貼復制的文本,接著點擊上方工具欄的“視圖”按鈕,然后下拉至“摘要”選項,點擊后,即可顯示文本的“統計信息”,統計信息即包括字符數,字數,行數,文檔長度等。
打開word,輸入文字后,如圖,在左下方即可顯示文本字數多少。
3, 如果你經常性需要知道文本字數大小,那么上述方式就顯得麻煩了,要么需要輸入網址或打開軟件,給大家推薦一個文本計數軟件(dragking),解壓后雙擊.ahk文件,可在軟件起動后,如果復制文本可自動顯示文本字數大小行數列數等,快捷方便。
下載地址:https://wwc.lanzouw.com/iXxGV0b1nuub,密碼:asuy
同時如下圖所示,此軟件還可進行多種設置,包括是否開機起動,取詞的快捷鍵,以及展示內容和位置進行設置。
好啦,今天達芬奇的分享就到這里啦,希望上述文本計數方式能幫到大家,同時如果你有使用過程中的問題也歡迎大家多多留言討論,點贊關注。
時候需要控制下文字數,不然就會溢出,頁面就會變樣不美觀。這時我們就可以用css控制字數,超出部分顯示省略號。可以不換行,超出部分顯示省略號,也可以可以換行,多行,超出部分顯示省略號。
1.不換行,超出部分顯示省略號
<!Doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"/> <title>用css控制字數,超出部分顯示省略號</title> <style type="text/css"> *{margin:0;padding:0;} body{width:1000px;margin:100px auto;} .box{ width:260px; /*超出部分就隱藏*/ overflow:hidden; /*不換行設定*/ white-space:nowrap; /*超出部分的文字顯示省略號*/ text-overflow:ellipsis; } </style> </head> <body> <div class="box">用css控制字數,超出部分顯示省略號用css控制字數,超出部分顯示省略號</div> </body> </html>
效果圖如下:
2.可以換行,多行,超出部分顯示省略號
<!Doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"/> <title>可以換行,多行,超出部分顯示省略號</title> <style type="text/css"> *{margin:0;padding:0;} body{width:1000px;margin:100px auto;} .box{ width:260px; display: -webkit-box; -webkit-box-orient: vertical; /*2行*/ -webkit-line-clamp: 2; overflow: hidden; } </style> </head> <body> <div class="box">1.用css控制字數,超出部分顯示省略號用css控制字數,超出部分顯示省略號</div> <div class="box">2.用css控制字數,超出部分顯示省略號用css控制字數,超出部分顯示省略號</div> </body> </html>
效果圖如下:
注:此方法適用于WebKit瀏覽器及移動端。
除注明外的文章,均為來源:湯久生博客,轉載請保留本文地址!
原文地址:http://tangjiusheng.com/divcss/169.html
*請認真填寫需求信息,我們會在24小時內與您取得聯系。