このドキュメントの内容は、検索用にコピーした Feishu ドキュメントであり、コンテンツの形式の互換性の問題があります。オリジナルのFeishu ドキュメントを参照することをお勧めします。
背景#
無料ユーザーとしてよくツイートを投稿する際、ツイートの制限に遭遇することがよくあります。
そのような場合、コンテンツを手動で分割する必要があります。また、分割されたツイートの内容をより明確にするために、通常、ツイートの進捗を示すような前置きのようなものを追加します。編集中に新しいコンテンツを追加したい場合、ツイートの分割を再調整する必要があります。このようなシナリオがますます増えるにつれて、このシナリオに対応するスクリプトを自動的に作成することを考えました。
この記事では、このスクリプトの作成プロセスと設計について簡単に説明します。スクリプトの実装はそれほど難しくありませんが、主に出力者として、後続の開発プロジェクトについて、ブログを通じてプロジェクトを反省し、蓄積することを目的としています。また、同様の要件を持つ人々に役立てることも期待しています。
リポジトリの場所:https://github.com/catwithtudou/x_tool
要件機能#
機能の要約:整形されたテキストを入力すると、ツイートの制限を超える場合は自動的に分割し、分割の進捗を示す前置き情報を追加します。
以下に注意してください:
- 分割のプロセスでは、元のテキストのレイアウトを変更せず、できるだけ多くのツイートの内容を埋める必要があります。
- 中文関連の文字の処理をサポートし、分割後に特殊な文字長による文字化けを回避する必要があります。
主要な設計#
ツイートの文字数制限#
まず、現在のツイートの文字数制限を把握して、後続の文字数計算を容易にします。
- 文字数制限は 280 文字で、リンクや画像は含まれません。
- 中文の文字は 2 文字としてカウントされ、その他の文字(句読点、スペース、改行などを含む)は 1 文字としてカウントされます。
Go での文字数計算#
ここで、いくつかの Go の前提条件を理解する必要があります。
- 文字列はデフォルトで UTF-8 エンコーディングで保存され、各中文は 3 バイトを占有します。
- 文字列を保存するために rune データ型配列を使用できます。rune 型は Unicode エンコードであり、各中文は 1 つの長さを占有します。
したがって、上記の情報に基づいて、以下のコードを使用してツイート内の文字列の長さを計算できます。
func calculateTwitterContentLen(content string) int {
// utf8.RuneCountInStringメソッドを使用して、文字列をrune配列に変換した長さを取得できます
runeCount := utf8.RuneCountInString(content)
return runeCount + (len(content)-runeCount)/2
}
二分探索による文字列の切り捨て位置の検索#
ツイートの内容が制限を超える場合、制限の長さに合致する最初の部分文字列を見つけます。以下に注意してください:
- ここで言及されている長さは「ツイート内の文字数」を指します。
- 文字の切り捨て位置を検索する前に、前置きの進捗(例:"(1/2)")文字の長さを計算する必要があります。
- 切り捨てを行う際に、中文文字のバイトが不完全であるために中文の文字化けが発生しないように注意する必要があります。
検索のパフォーマンスを向上させるために、この場合は二分探索を使用して、ツイート内の制限に合致する最初の位置を見つけます。したがって、以下のコードを使用します。
// splitFirstMaxTwitterContentは、対応する最初の切り捨て位置を見つけて、切り捨て後の左右の文字列を出力します
func splitFirstMaxTwitterContent(content string) (left string, right string) {
contentLen := len(content)
if contentLen <= twitterMaxLength {
return content, ""
}
// ここでの長さの計算はすべてUnicodeの方法で行われることに注意し、中文の文字化けを回避します
runeContent := []rune(content)
runeIdx := sort.Search(len(runeContent), func(i int) bool {
_, isExceed := calculateTwitterRuneContentLen(runeContent[:i])
return isExceed
})
return string(runeContent[:runeIdx]), string(runeContent[runeIdx:])
}
func calculateTwitterRuneContentLen(runeContent []rune) (int, bool) {
tweetLen := len(runeContent) + (len(string(runeContent))-len(runeContent))/2
return tweetLen, tweetLen >= twitterMaxLength
}
使用方法#
- コンパイルされた main プログラムを実行します
- ターミナルのプロンプトに従って、投稿するツイートの内容を入力します。入力が完了したら、改行して "exit" と入力して入力を終了します。
- 最後に、ツイートの内容が制限の長さを超えていない場合は元のテキストが返されます。超過している場合、プログラムは自動的に切り捨てられたツイートの内容を出力し、前置きの進捗が追加されます。
./main
===================================================
ツイートの内容を入力してください(最後に「exit」と入力して入力を終了します):
これはテストテキストです:以下の文は「正見」から引用されています。
無意識の中で、私たちは自分自身がもう何も修理する必要がない状態に到達することを期待しています。いつか「幸せな人生を送る」ということです。私たちは「解決」という概念を信じています。まるで私たちがこれまでのすべての経験、この時点までの人生がリハーサルであるかのように。大規模なパフォーマンスはまだ始まっていません。多くの人にとって、この終わりのない処理と再配置、およびバージョンアップは生活の定義です。
私たちはしばしば次のように考えることもあります:私たちが死んだ後も、世界は存在し続けます。同じ太陽が地球を照らし続け、同じ惑星が回り続けるでしょう。私たちは、天地創造以来、それらが常にそうであったと考えています。私たちの子供たちはこの地球を受け継ぐでしょう。これは、私たちが絶えず変化する世界とすべての現象に対してどれほど無知であるかを示しています。
exit
===================================================
現在のツイートの長さ:566
ツイートの長さ制限を超えています:true
===================================================
今、切り捨てられたツイートの内容に戻ります
>>>>>>>「1番目のツイートの内容」
(1/3)これはテストテキストです:以下の文は「正見」から引用されています。
無意識の中で、私たちは自分自身がもう何も修理する必要がない状態に到達することを期待しています。いつか「幸せな人生を送る」ということです。私たちは「解決」という概念を信じています。まるで私たちがこれまでのすべての経験、この時点までの人生がリハーサルであるかのように。大規模なパフォーマンスはまだ始まっていません。多くの人にとって、この終わりのない処理
>>>>>>>「2番目のツイートの内容」
(2/3)と再配置、およびバージョンアップは生活の定義です。
私たちはしばしば次のように考えることもあります:私たちが死んだ後も、世界は存在し続けます。同じ太陽が地球を照らし続け、同じ惑星が回り続けるでしょう。私たちは、天地創造以来、それらが常にそうであったと考えています。私たちの子供たちはこの地球を受け継ぐでしょう。これは、私たちが絶えず変化する世界とすべての現象に対してどれほど無知であるかを示しています。
>>>>>>>「3番目のツイートの内容」
(3/3)。
>>>>>>>「ツイートの切り捨ての終わり」
追加の拡張#
- ターミナルのインタラクションの最適化
- ツイートの自動投稿
- レイアウトの最適化
- .....