我确信我们所有人都在Facebook状态(或其他地方)看过省略号,然后点击“显示更多”并且只有另外2个字符左右。我猜这是因为懒惰的编程,因为肯定有一种理想的方法。
我将细长字符[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;
}
语言并不重要,但标记为Java,因为这是我最感兴趣的内容。
答案 0 :(得分:76)
我喜欢让“瘦”字符算作半个字符的想法。简单而且近似很好。
然而,大多数椭圆化的主要问题是(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) + "...";
}
算法测试如下:
答案 1 :(得分:48)
我很震惊没有人提到Commons Lang StringUtils#abbreviate()。
更新:是的,它没有考虑到苗条的角色,但我不同意这一点,因为考虑到每个人都有不同的屏幕和字体设置,并且本页面上的大部分人都可能正在寻找像上面那样维护图书馆。
答案 2 :(得分:26)
您似乎可以从Java图形上下文FontMetrics
获得更准确的几何图形。
附录:在解决这个问题时,可能有助于区分模型和视图。该模型是String
,是UTF-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();
}
});
}
}
答案 3 :(得分:10)
如果您正在谈论一个网站 - 即输出HTML / JS / CSS,您可以抛弃所有这些解决方案,因为有一个纯CSS解决方案。
text-overflow:ellipsis;
这不仅仅是将这种风格添加到CSS中,因为它会与其他CSS交互;例如,它要求元素溢出:隐藏;如果你想把你的文字放在一行,white-space:nowrap;
也是好的。
我有一个样式表,如下所示:
.myelement {
word-wrap:normal;
white-space:nowrap;
overflow:hidden;
-o-text-overflow:ellipsis;
text-overflow:ellipsis;
width: 120px;
}
你甚至可以有一个“阅读更多”按钮,只需运行一个javascript函数来改变样式,宾果游戏,盒子将重新调整大小,全文将可见。 (在我的情况下,我倾向于使用html title属性作为全文,除非它可能会变得很长)
希望有所帮助。这是一个更简单的解决方案,试图弄乱计算文本大小并截断它,以及所有这些。 (当然,如果您正在编写非基于Web的应用程序,您可能仍需要这样做)
此解决方案有一个缺点:Firefox不支持省略号样式。烦人,但我认为不重要 - 它仍然正确截断文本,因为溢出处理:隐藏,它只是不显示省略号。它可以在所有其他浏览器中工作(包括IE,一直回到IE5.5!),所以Firefox还没有做到这一点有点烦人。希望新版本的Firefox能很快解决这个问题。
<强> [编辑] 强>
人们仍在对这个答案进行投票,所以我应该编辑它以注意Firefox现在支持省略号样式。 Firefox 7中添加了该功能。如果您使用的是早期版本(FF3.6和FF4仍然有一些用户),那么您运气不好,但大多数FF用户现在都可以。这里有更多详细信息:text-overflow:ellipsis in Firefox 4? (and FF5)
答案 4 :(得分:4)
对我而言,这将是理想的 -
public static String ellipsis(final String text, int length)
{
return text.substring(0, length - 3) + "...";
}
我不担心每个角色的大小,除非我真的知道它将在何处以及以什么字体显示。许多字体都是固定宽度的字体,每个字符都有相同的尺寸。
即使它是一个可变宽度的字体,如果你算'i','l'要占宽度的一半,那么为什么不算'w''m'取宽度的两倍?字符串中混合使用这些字符通常会平均其大小的影响,我宁愿忽略这些细节。明智地选择“长度”的值最重要。
答案 5 :(得分:4)
public static String getTruncated(String str, int maxSize){
int limit = maxSize - 3;
return (str.length() > maxSize) ? str.substring(0, limit) + "..." : str;
}
答案 6 :(得分:4)
这个怎么样(得到一串50个字符):
text.replaceAll("(?<=^.{47}).*$", "...");
答案 7 :(得分:3)
如果您担心省略号仅隐藏极少数字符,为什么不检查该条件?
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 + 20)
{
return text.substring(0, length - 3) + "...";
}
return text;
}
答案 8 :(得分:3)
我会选择类似于您所拥有的标准型号的东西。我不打扰字符宽度的事情 - 因为@Gopi说它最终可能会让所有人平衡。我要做的就是新的另一个参数叫做“minNumberOfhiddenCharacters”(可能稍微冗长一点)。然后在做省略号检查时,我会做类似的事情:
if (text.length() > length+minNumberOfhiddenCharacters)
{
return text.substring(0, length - 3) + "...";
}
这意味着如果你的文字长度是35,你的“长度”是30,你隐藏的最小字符数是10,那么你就可以得到你的字符串。如果要隐藏的最小字符数为3,那么您将获得省略号而不是这三个字符。
要注意的主要是我已经颠覆了“长度”的含义,因此它不再是最大长度。输出字符串的长度现在可以是30个字符(当文本长度> 40时)到40个字符(当文本长度为40个字符长时)。实际上,我们的最大长度变为length + minNumberOfhiddenCharacters。当原始字符串小于30时,字符串当然可以短于30个字符,但这是一个我们应该忽略的无聊案例。
如果你想要长度是最快的,那么你需要更多的东西:
if (text.length() > length)
{
if (text.length() - length < minNumberOfhiddenCharacters-3)
{
return text.substring(0, text.length() - minNumberOfhiddenCharacters) + "...";
}
else
{
return text.substring(0, length - 3) + "...";
}
}
所以在这个例子中,如果text.length()是37,长度是30,minNumberOfhiddenCharacters = 10,那么我们将进入内部的第二部分if和得到27个字符+ ...来制作30.这是实际上就像我们进入循环的第一部分一样(这是一个符号,我们的边界条件是正确的)。如果文本长度是36,我们将得到26个字符+省略号给我们29个字符,其中10个隐藏。
我在辩论是否重新安排一些比较逻辑会使它更直观,但最终决定保留原样。您可能会发现text.length() - minNumberOfhiddenCharacters < length-3
使您的工作变得更加明显。
答案 9 :(得分:3)
在我看来,没有像素数学就无法取得好成绩。
因此,当您处于Web应用程序上下文(如Facebook)时,Java可能是解决此问题的错误方法。
我会去javascript。由于Javascript不是我感兴趣的主要领域,我无法判断this是否是一个好的解决方案,但它可能会给你一个指针。
答案 10 :(得分:2)
使用Guava的com.google.common.base.Ascii.truncate(CharSequence, int, String)方法:
Ascii.truncate("foobar", 7, "..."); // returns "foobar"
Ascii.truncate("foobar", 5, "..."); // returns "fo..."
答案 11 :(得分:0)
大多数这种解决方案都没有考虑字体指标,这是我已经使用多年的非常简单但有效的Java swing解决方案。
val qrImage = findViewById<ImageView>(R.id.img_qr_code)
val myBitmap = QRCode.from("www.example.org").bitmap()
qrImage.setImageBitmap(myBitmap)
答案 12 :(得分:-1)
您也可以像这样简单地实现:
mb_strimwidth($string, 0, 120, '...')
谢谢。