2009-05-27 17 views
6

Tôi muốn phù hợp với chuỗi thành chiều rộng cụ thể. Ví dụ: "Xin chào thế giới" -> "... thế giới", "Xin chào ...", "Anh ấy ... rld".

Bạn có biết nơi tôi có thể tìm mã cho điều đó không? Đó là một thủ thuật gọn gàng, rất hữu ích để đại diện cho thông tin, và tôi muốn thêm nó vào các ứng dụng của tôi (tất nhiên).

Chỉnh sửa: Rất tiếc, tôi quên đề cập đến phần Phông chữ. Không chỉ cho các chuỗi có chiều rộng cố định mà còn theo khuôn mặt phông chữ.Làm cách nào để chuyển đổi chuỗi thành dạng viết tắt?

+0

Bạn đang sử dụng MFC, hoặc ...? – Smashery

Trả lời

8

Đó là một thuật toán khá đơn giản để viết cho mình nếu bạn không thể tìm thấy nó ở bất cứ đâu - những giả sẽ là một cái gì đó như:

if theString.Length > desiredWidth: 
    theString = theString.Left(desiredWidth-3) + "..."; 

hoặc nếu bạn muốn ellipsis vào lúc bắt đầu của chuỗi, dòng thứ hai sẽ là:

theString = "..." + theString.Right(desiredWidth-3); 

hoặc nếu bạn muốn nó ở giữa:

theString = theString.Left((desiredWidth-3)/2) + "..." + theString.Right((desiredWidth-3)/2 + ((desiredWidth-3) mod 2)) 

Chỉnh sửa:
Tôi giả sử bạn đang sử dụng MFC. Vì bạn muốn nó với phông chữ, bạn có thể sử dụng hàm CDC::GetOutputTextExtent. Hãy thử:

CString fullString 
CSize size = pDC->GetOutputTextExtent(fullString); 
bool isTooWide = size.cx > desiredWidth; 

Nếu quá lớn, thì bạn có thể tìm kiếm để thử và tìm chuỗi dài nhất bạn có thể vừa; và nó có thể là một tìm kiếm thông minh như bạn muốn - ví dụ, bạn chỉ có thể thử "Hello Worl ..." và sau đó "Hello Wor ..." và sau đó "Hello Wo ..."; loại bỏ một ký tự cho đến khi bạn tìm thấy nó phù hợp. Ngoài ra, bạn có thể thực hiện binary search - thử "Xin chào Worl ..." - nếu điều đó không hiệu quả, thì chỉ cần sử dụng một nửa ký tự của văn bản gốc: "Xin chào ..." - nếu điều đó phù hợp, hãy thử nửa chừng và: "Xin chào Wo ..." cho đến khi bạn tìm thấy lâu nhất mà vẫn còn phù hợp. Hoặc bạn có thể thử một số dựa trên kinh nghiệm lập dự toán (chia tổng chiều dài bằng chiều dài mong muốn, tương ứng ước tính số lượng yêu cầu của nhân vật, và tìm kiếm từ đó

Giải pháp đơn giản là một cái gì đó như:.

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; 
} 
+0

Có, MFC của nó và tôi sẽ thực hiện một * thông minh * phù hợp. Tôi muốn xem liệu có triển khai hiệu quả cho loại nội dung đó trước khi cố gắng thực hiện của riêng tôi hay không. Nhưng tôi đoán tôi phải ngồi xuống và sáng tạo :) –

+0

Câu trả lời tuyệt vời, đầy đủ, hữu ích. –

+0

Cảm ơn, Aidan :-) – Smashery

2

Nó thực sự khá tầm thường; Tôi không nghĩ rằng bạn sẽ tìm thấy mã cụ thể trừ khi bạn có một cái gì đó có cấu trúc hơn trong tâm trí.

Bạn về cơ bản muốn:

  1. để có được độ dài của chuỗi bạn có, và chiều rộng cửa sổ.
  2. tìm ra số lượng charaters bạn có thể lấy từ chuỗi gốc, về cơ bản sẽ có chiều rộng cửa sổ-3. Gọi số k.
  3. Tùy thuộc vào việc bạn muốn đặt dấu chấm lửng ở giữa hay ở đầu bên phải, hãy lấy các ký tự tầng đầu tiên (k/2) từ một đầu, nối với "...", rồi nối với dấu cuối cùng ký tự sàn (k/2) (có thể có thêm một ký tự cần thiết do phân chia); hoặc lấy ký tự k đầu tiên, được theo dõi bởi "...".
2

Tôi nghĩ Câu trả lời của Smashery là một khởi đầu tốt.Một cách để có được kết quả cuối cùng là viết một số mã thử nghiệm với một số đầu vào thử nghiệm và các đầu ra mong muốn. Một khi bạn có một bộ tốt các thiết lập kiểm tra, bạn có thể thực hiện mã thao tác chuỗi của bạn cho đến khi bạn nhận được tất cả các bài kiểm tra của bạn để vượt qua.

1
  • Tính chiều rộng của văn bản ( dựa trên font)

Trong trường hợp bạn đang sử dụng MFC API GetOutputTextExtent sẽ giúp bạn có giá trị.

  • nếu chiều rộng vượt quá cho chiều rộng cụ thể, tính toán chiều rộng hình elip đầu tiên:

    ellipseWidth = tính toán độ rộng của (...)

  • Tháo phần chuỗi với chiều rộng ellipseWidth từ cuối và nối thêm hình elip.

    cái gì đó như: Xin chào ...

+0

Có thể bạn sẽ thấy rằng chiều rộng (văn bản) + chiều rộng (dấu chấm lửng) không bằng chiều rộng (văn bản + dấu chấm lửng). Điều này là bởi vì một số glyphs chồng lên nhau khi chúng được trả lại để làm cho nó trông đẹp hơn. Vì vậy, bạn không thể chỉ trừ đi độ rộng hình elip từ độ rộng mong muốn - bạn phải kiểm tra chiều dài đầy đủ (bao gồm dấu ba chấm) – Smashery

+0

Vâng, đúng vậy. Trong một vòng lặp, chúng ta cần phải kiểm tra xem kích thước với hình elip có tương ứng với kích thước đã cho hay không (để xác định các ký tự cần xóa khỏi chuỗi). –

1

Đối với những người quan tâm cho một thói quen hoàn thành, đây là tôi câu trả lời:

/** 
* 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; 
    */ 
} 
+0

+1 Làm tốt lắm! Chỉ cần một lời cảnh cáo: Tôi phát hiện ra khi làm một điều tương tự trong một ngôn ngữ khác mà khi bạn kết hợp các chữ cái trong một phông chữ, họ có thể không nhất thiết phải bằng tổng của độ dài cá nhân của họ. Ví dụ, khi bạn đặt 'W' bên cạnh 'A', trình kết xuất phông chữ có thể đặt chúng gần nhau hơn một chút để làm cho nó trông đẹp hơn cho mắt người. Vì vậy, chiều rộng ('W') + chiều rộng ('A') có thể không bằng chiều rộng ('WA'). Đối với các chuỗi nhỏ, nó có lẽ sẽ chỉ tạo ra sự khác biệt của một vài điểm ảnh, nhưng đối với các pixel dài hơn, nó có thể khá đáng chú ý. – Smashery

+0

Cảm ơn. Ok, tôi sẽ kiểm tra. –

+0

Cũng nhờ có gợi ý tìm kiếm nhị phân trong câu trả lời của bạn, ý tưởng rất hay! Có lẽ tôi sẽ thực hiện nó khi tôi có thời gian - tìm kiếm nhị phân có thể phức tạp vì vậy tôi bỏ qua nó trong phiên bản đầu tiên :) –

1

Nếu bạn chỉ muốn "tiêu chuẩn" định dạng dấu chấm lửng, ("Xin chào ...") bạn có thể sử dụng chức năng DrawText (hoặc tương đương MFC function) qua DT_END_ELLIPSIS.

Kiểm tra cũng DT_WORD_ELLIPSIS (nó cắt bớt bất kỳ từ nào không vừa với hình chữ nhật và thêm dấu ba chấm) hoặc DT_PATH_ELLIPSIS (trình duyệt nào sẽ hiển thị đường dẫn dài).

+0

Tôi nghĩ rằng tôi cần một thuật toán khi tôi đăng câu hỏi, nhưng cảm ơn anyway, đó là siêu thuận tiện! –

+0

Tôi tìm thấy câu hỏi của bạn đang tìm kiếm cùng một vấn đề, tìm kiếm trên GetDNExtent MSDN, và tìm thấy tùy chọn này đơn giản trong DrawText. Vì tôi cần dấu chấm lửng ở cuối, cho tôi nó là đủ, và có lẽ đối với một số độc giả trong tương lai cũng sẽ là đủ! –

+0

vâng, đó là cách để làm điều đó nếu chúng ta sử dụng GDI và cần cắt tỉa văn bản cơ bản. –

Các vấn đề liên quan