文字列を特定の幅に合わせたい。例:「Helloworld」->「... world」、「Hello ...」、「He...rld」。
そのためのコードがどこにあるか知っていますか?これは巧妙なトリックであり、情報を表現するのに非常に役立ちます。アプリケーションに追加したいと思います(もちろん)。
編集:申し訳ありませんが、フォントの部分について言及するのを忘れました。固定幅の文字列だけでなく、フォントの面にも応じます。
6 に答える
どこにも見つからない場合に自分で作成するのは非常に単純なアルゴリズムです。擬似コードは次のようになります。
if theString.Length > desiredWidth:
theString = theString.Left(desiredWidth-3) + "...";
または、文字列の先頭に省略記号が必要な場合、その2行目は次のようになります。
theString = "..." + theString.Right(desiredWidth-3);
または、途中で必要な場合:
theString = theString.Left((desiredWidth-3)/2) + "..." + theString.Right((desiredWidth-3)/2 + ((desiredWidth-3) mod 2))
編集:
MFCを使用していると仮定します。フォントでそれが必要なので、 CDC::GetOutputTextExtent関数を使用できます。試す:
CString fullString
CSize size = pDC->GetOutputTextExtent(fullString);
bool isTooWide = size.cx > desiredWidth;
それが大きすぎる場合は、検索を実行して、収まる最長の文字列を見つけることができます。たとえば、「Hello Worl ...」、「Hello Wor ...」、「Hello Wo...」の順に試してみてください。収まるまで1文字を削除します。または、バイナリ検索を実行することもできます-「HelloWorl ...」を試してください-それが機能しない場合は、元のテキストの半分の文字を使用してください:「Hello ...」-それが収まる場合は、途中で試してくださいそれと:「HelloWo...」あなたがまだ収まる最長のものを見つけるまで。または、ヒューリスティックな見積もりを試すこともできます(全長を目的の長さで割り、必要な文字数を比例的に見積もり、そこから検索します。
簡単な解決策は次のようなものです。
unsigned int numberOfCharsToUse = fullString.GetLength();
bool isTooWide = true;
CString ellipsis = "...";
while (isTooWide)
{
numberOfCharsToUse--;
CString string = fullString.Left(numberOfCharsToUse) + ellipsis;
CSize size = pDC->GetOutputTextExtent(string);
isTooWide = size.cx > desiredWidth;
}
それは本当に些細なことです。もっと構造化されたものを考えていない限り、特定のコードを見つけることはできないと思います。
あなたは基本的に欲しいです:
- あなたが持っている文字列の長さとウィンドウの幅を取得します。
- 元の文字列から取得できる文字数を計算します。これは基本的にウィンドウ幅3になります。それをkと呼びます。
- 省略記号を中央に配置するか右端に配置するかに応じて、一方の端から1階(k / 2)の文字を取得し、「...」で連結してから、最後の階(k / 2)文字(分割のためにもう1文字必要になる可能性があります); または、最初のk文字を取り、その後に「...」を続けます。
Smasheryの答えは良いスタートだと思います。最終結果を得る1つの方法は、いくつかのテスト入力と目的の出力を使用してテストコードを作成することです。テストのセットアップが適切に行われたら、すべてのテストに合格するまで文字列操作コードを実装できます。
- テキストの幅を計算します(フォントに基づいて)
MFCを使用している場合、APIGetOutputTextExtentが値を取得します。
幅が指定された特定の幅を超える場合は、最初に楕円の幅を計算します。
ellipseWidth =(...)の幅を計算します
幅ellipseWidthの文字列部分を端から削除し、楕円を追加します。
のようなもの:こんにちは...
完全なルーチンに興味がある人のために、これは私の答えです:
/**
* Returns a string abbreviation
* example: "hello world" -> "...orld" or "hell..." or "he...rd" or "h...rld"
*
* style:
0: clip left
1: clip right
2: clip middle
3: pretty middle
*/
CString*
strabbr(
CDC* pdc,
const char* s,
const int area_width,
int style )
{
if ( !pdc || !s || !*s ) return new CString;
int len = strlen(s);
if ( pdc->GetTextExtent(s, len).cx <= area_width ) return new CString(s);
int dots_width = pdc->GetTextExtent("...", 3).cx;
if ( dots_width >= area_width ) return new CString;
// My algorithm uses 'left' and 'right' parts of the string, by turns.
int n = len;
int m = 1;
int n_width = 0;
int m_width = 0;
int tmpwidth;
// fromleft indicates where the clip is done so I can 'get' chars from the other part
bool fromleft = (style == 3 && n % 2 == 0)? false : (style > 0);
while ( TRUE ) {
if ( n_width + m_width + dots_width > area_width ) break;
if ( n <= m ) break; // keep my sanity check (WTF), it should never happen 'cause of the above line
// Here are extra 'swap turn' conditions
if ( style == 3 && (!(n & 1)) )
fromleft = (!fromleft);
else if ( style < 2 )
fromleft = (!fromleft); // (1)'disables' turn swapping for styles 0, 1
if ( fromleft ) {
pdc->GetCharWidth(*(s+n-1), *(s+n-1), &tmpwidth);
n_width += tmpwidth;
n--;
}
else {
pdc->GetCharWidth(*(s+m-1), *(s+m-1), &tmpwidth);
m_width += tmpwidth;
m++;
}
fromleft = (!fromleft); // (1)
}
if ( fromleft ) m--; else n++;
// Final steps
// 1. CString version
CString* abbr = new CString;
abbr->Format("%*.*s...%*.*s", m-1, m-1, s, len-n, len-n, s + n);
return abbr;
/* 2. char* version, if you dont want to use CString (efficiency), replace CString with char*,
new CString with _strdup("") and use this code for the final steps:
char* abbr = (char*)malloc(m + (len-n) + 3 +1);
strncpy(abbr, s, m-1);
strcpy(abbr + (m-1), "...");
strncpy(abbr+ (m-1) + 3, s + n, len-n);
abbr[(m-1) + (len-n) + 3] = 0;
return abbr;
*/
}