用省略号截断字符串的理想方法

Amy*_*y B 56 java ellipsis

我相信我们所有人都在Facebook状态(或其他地方)看过省略号,然后点击"显示更多"并且只有另外两个字符左右.我猜这是因为懒惰的编程,因为肯定有一种理想的方法.

我将细长的字符[iIl1]称为"半字符",但是当它们几乎不隐藏任何字符时,这并不会使省略号看起来很傻.

有理想的方法吗?这是我的:

/**
 * Return a string with a maximum length of <code>length</code> characters.
 * If there are more than <code>length</code> characters, then string ends with an ellipsis ("...").
 *
 * @param text
 * @param length
 * @return
 */
public static String ellipsis(final String text, int length)
{
    // The letters [iIl1] are slim enough to only count as half a character.
    length += Math.ceil(text.replaceAll("[^iIl]", "").length() / 2.0d);

    if (text.length() > length)
    {
        return text.substring(0, length - 3) + "...";
    }

    return text;
}
Run Code Online (Sandbox Code Playgroud)

语言并不重要,但标记为Java,因为这是我最感兴趣的内容.

aio*_*obe 80

我喜欢让"瘦"字符算作半个字符的想法.简单而且很好的近似.

然而,大多数椭圆化的主要问题是(imho)他们在中间砍掉了单词.这是一个考虑词边界的解决方案(但不会涉及像素数学和Swing-API).

private final static String NON_THIN = "[^iIl1\\.,']";

private static int textWidth(String str) {
    return (int) (str.length() - str.replaceAll(NON_THIN, "").length() / 2);
}

public static String ellipsize(String text, int max) {

    if (textWidth(text) <= max)
        return text;

    // Start by chopping off at the word before max
    // This is an over-approximation due to thin-characters...
    int end = text.lastIndexOf(' ', max - 3);

    // Just one long word. Chop it off.
    if (end == -1)
        return text.substring(0, max-3) + "...";

    // Step forward as long as textWidth allows.
    int newEnd = end;
    do {
        end = newEnd;
        newEnd = text.indexOf(' ', end + 1);

        // No more spaces.
        if (newEnd == -1)
            newEnd = text.length();

    } while (textWidth(text.substring(0, newEnd) + "...") < max);

    return text.substring(0, end) + "...";
}
Run Code Online (Sandbox Code Playgroud)

算法测试如下所示:

在此输入图像描述

  • 我真的很喜欢这一个. (3认同)
  • 您可能需要使用省略号字符“...”而不是三个句点,因为该行可能会在句点之间精确断开。对上述代码进行此更改时,请将所有出现的“3”更改为“1”。 (2认同)

Ada*_*ent 54

我很震惊没有人提到Commons Lang StringUtils #breviate().

更新:是的,它没有考虑到苗条的角色,但我不同意这一点,考虑到每个人都有不同的屏幕和字体设置,这个页面上的大部分人可能正在寻找像这样的维护库以上.

  • 大概吧.我错过了你的苗条角色参考,但我个人认为它很荒谬,并没有考虑**i18n**.它的**不是**理想的**方法,现在人们要复制并粘贴上面的代码,当有一个库已经以确定的方式做到了...顺便说一下你错过了因为"t"是我的屏幕上很苗条. (4认同)
  • 这不符合我的问题。 (2认同)

tra*_*god 27

看起来您可能从Java图形上下文中获得更准确的几何FontMetrics.

附录:在解决这个问题时,可能有助于区分模型和视图.该模型是一个StringUTF-16代码点的有限序列,而视图是一系列字形,在某些设备上以某种字体呈现.

在Java的特定情况下,可以使用SwingUtilities.layoutCompoundLabel()来实现翻译.下面的示例拦截布局调用BasicLabelUI以演示效果.可以在其他环境中使用效用方法,但FontMetrics必须根据经验确定适当的方法.

替代文字

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicLabelUI;

/** @see http://stackoverflow.com/questions/3597550 */
public class LayoutTest extends JPanel {

    private static final String text =
        "A damsel with a dulcimer in a vision once I saw.";
    private final JLabel sizeLabel = new JLabel();
    private final JLabel textLabel = new JLabel(text);
    private final MyLabelUI myUI = new MyLabelUI();

    public LayoutTest() {
        super(new GridLayout(0, 1));
        this.setBorder(BorderFactory.createCompoundBorder(
            new LineBorder(Color.blue), new EmptyBorder(5, 5, 5, 5)));
        textLabel.setUI(myUI);
        textLabel.setFont(new Font("Serif", Font.ITALIC, 24));
        this.add(sizeLabel);
        this.add(textLabel);
        this.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                sizeLabel.setText(
                    "Before: " + myUI.before + " after: " + myUI.after);
            }
        });
    }

    private static class MyLabelUI extends BasicLabelUI {

        int before, after;

        @Override
        protected String layoutCL(
            JLabel label, FontMetrics fontMetrics, String text, Icon icon,
            Rectangle viewR, Rectangle iconR, Rectangle textR) {
            before = text.length();
            String s = super.layoutCL(
                label, fontMetrics, text, icon, viewR, iconR, textR);
            after = s.length();
            System.out.println(s);
            return s;
        }
    }

    private void display() {
        JFrame f = new JFrame("LayoutTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new LayoutTest().display();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)


Spu*_*ley 10

如果您正在谈论一个网站 - 即输出HTML/JS/CSS,您可以抛弃所有这些解决方案,因为有一个纯CSS解决方案.

text-overflow:ellipsis;
Run Code Online (Sandbox Code Playgroud)

它不仅仅是将这种风格添加到CSS中,因为它会与其他CSS交互; 例如,它要求元素溢出:隐藏; 如果你想把你的文字放在一行,white-space:nowrap;也是好的.

我有一个样式表,如下所示:

.myelement {
  word-wrap:normal;
  white-space:nowrap;
  overflow:hidden;
  -o-text-overflow:ellipsis;
  text-overflow:ellipsis;
  width: 120px;
}
Run Code Online (Sandbox Code Playgroud)

你甚至可以有一个"阅读更多"按钮,只需运行一个javascript函数来改变样式,宾果游戏,盒子将重新调整大小,全文将可见.(在我的情况下,我倾向于使用html title属性作为全文,除非它可能会变得很长)

希望有所帮助.这是一个更简单的解决方案,试图弄乱计算文本大小并截断它,以及所有这些.(当然,如果您正在编写非基于Web的应用程序,您可能仍需要这样做)

这个解决方案有一个缺点:Firefox不支持省略号样式.烦人,但我认为不重要 - 它仍然正确截断文本,因为溢出处理:隐藏,它只是不显示省略号.它可以在所有其他浏览器中工作(包括IE,一直回到IE5.5!),所以Firefox还没有做到这一点有点烦人.希望新版本的Firefox能很快解决这个问题.

[编辑]
人们仍在对这个答案进行投票,所以我应该编辑它以注意Firefox现在支持省略号样式.Firefox 7中添加了该功能.如果您使用的是早期版本(FF3.6和FF4仍然有一些用户),那么您运气不好,但大多数FF用户现在都可以.这里有更多细节:文本溢出:Firefox 4中的省略号?(和FF5)


Adr*_*ker 6

使用 Guava 的com.google.common.base.Ascii.truncate(CharSequence, int, String)方法:

Ascii.truncate("foobar", 7, "..."); // returns "foobar"
Ascii.truncate("foobar", 5, "..."); // returns "fo..."
Run Code Online (Sandbox Code Playgroud)


Gop*_*opi 5

对我来说,这将是理想的-

 public static String ellipsis(final String text, int length)
 {
     return text.substring(0, length - 3) + "...";
 }
Run Code Online (Sandbox Code Playgroud)

除非我真的知道它将在何处以哪种字体显示,否则我不会担心每个字符的大小。许多字体是固定宽度的字体,其中每个字符都具有相同的尺寸。

即使它是可变宽度的字体,并且如果您将'i','l'视为宽度的一半,那么为什么不将'w''m'视为宽度的两倍呢?在字符串中混合使用这些字符通常会平均影响其大小,因此,我宁愿忽略这些细节。明智地选择“长度”的值最为重要。

  • 这可能会引发 IndexOutOfBoundsException。在使用子字符串之前应该测试字符串的长度。 (2认同)

yeg*_*256 5

怎么样(获取50个字符的字符串):

text.replaceAll("(?<=^.{47}).*$", "...");
Run Code Online (Sandbox Code Playgroud)