首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何确定Unicode字符的宽度?

如何确定Unicode字符的宽度?
EN

Stack Overflow用户
提问于 2022-02-22 21:03:11
回答 4查看 740关注 0票数 1

我和一个朋友正在用java编写我们自己的控制台,但是由于unicode字符的宽度无法准确确定,我们在正确调整行时遇到了问题。这就导致了这样的问题:不仅unicode的行,而且下面的行都被移位了。

有办法确定独角兽的宽度吗?

问题的截图可以在下面找到。

它应该是这样的:https://abload.de/img/richtigslkmg.jpeg

这是终端中的一个例子:https://abload.de/img/terminal7dj5o.jpeg

这是PowerShell:https://abload.de/img/powershelln7je0.jpeg中的一个例子

这是Visual代码中的一个示例:https://abload.de/img/visualstudiocode4xkuo.jpeg

这是Putty中的一个例子:https://abload.de/img/putty0ujsk.png

编辑:

很抱歉,这个问题不清楚。

这是关于显示宽度,在示例中,我试图确定显示长度,使每一行具有相同的长度。函数real_length是计算/确定和返回显示宽度。

下面是示例代码:

代码语言:javascript
复制
public static void main(String[] args) {
    String[] tests = {
        "Peter",
        "SHGAMI",
        "Marcel №1",
        "",
        "‍❤️‍",
        "‍❤️‍‍",
        "‍‍"
    };
    for(String test : tests) test(test);
}

public static void test(String text) {
    int max = 20;
    for(int i = 0; i < max;i++) System.out.print("#");
    System.out.println();
    System.out.print(text);
    int length = real_length(text);
    for(int i = 0; i < max - length;i++) System.out.print("#");
    System.out.println();
}

public static int real_length(String text) {
    return text.length();
}
EN

回答 4

Stack Overflow用户

发布于 2022-02-22 22:49:24

tl;dr

使用代码点而不是char。避免调用String#length

代码语言:javascript
复制
input 
+ 
"#".repeat( targetLength - input.codePoints().toArray().length ) 

详细信息

你的问题没有显示任何密码。所以我只能猜出你在做什么,问题是什么。

避免char

我猜您的目标是在需要时追加一定数量的数字符号字符,以生成固定长度的文本行。

我猜问题在于您使用的是遗留的char类型,或者它的包装类Character。从Java2开始,char类型基本上就被打破了。作为一个16位的值,char在物理上无法表示大多数字符。

使用代码点号

相反,在处理单个字符时使用码点整数。代码点是永久分配给14万多个在Unicode中定义的字符中的每一个的数字。

Java 5+中的各种代码点相关方法被添加到不同的类中:StringStringBuilderCharacter等。

在这里,我们使用String#codePoints获取代码点的IntStream,这是源中每个字符的一个元素。并且我们使用StringBuilder#appendCodePoint为我们的最终结果字符串收集代码点。

代码语言:javascript
复制
final int targetLength = 10;
final int fillerCodePoint = "#".codePointAt( 0 ); // Annoying zero-based index counting.
String input = "";

int[] codePoints = input.codePoints().toArray();
StringBuilder stringBuilder = new StringBuilder();
for ( int index = 0 ; index < targetLength ; index++ )
{
    if ( index < codePoints.length )
    {
        stringBuilder.appendCodePoint( codePoints[ index ] );
    } else
    {
        stringBuilder.appendCodePoint( fillerCodePoint );
    }
}

或者,使用一个for来缩短这个三元算子循环。

代码语言:javascript
复制
for ( int index = 0 ; index < targetLength ; index++ )
{
    int codePoint = ( index < codePoints.length ) ? codePoints[ index ] : fillerCodePoint;
    stringBuilder.appendCodePoint( codePoint );
}

报告结果。

代码语言:javascript
复制
System.out.println( Arrays.toString( codePoints ) );
String output = stringBuilder.toString();
System.out.println( "output = " + output );

128567,129312,129313 输出= #######

可能有一种聪明的方法可以更简单地使用流和lambda编写代码,但我现在想不出其中的一个。

而且,我们可以巧妙地在Java11+中使用String#repeat方法。

代码语言:javascript
复制
String output = input + "#".repeat( targetLength - input.codePoints().toArray().length ) ;
票数 1
EN

Stack Overflow用户

发布于 2022-02-25 20:48:26

不幸的是,对于你看似简单的问题,没有简单的解决办法,原因有几点:

  • 根据所使用的字体,控制台上呈现的字符的宽度可能(而且可能会)有所不同。因此,为了计算宽度,代码需要确定或假定目标字体。
  • System.out只是一个不知道或不关心字体和字符宽度的PrintStream,所以任何解决方案都必须独立于此。
  • 即使您可以确定控制台上使用的字体,并且可以确定每个字符在特定字体中呈现的宽度,这对您有什么帮助呢?了解宽度的变化可能会让您巧妙地调整正在呈现的线条,使它们对齐,但这很可能是不切实际的。
  • 一个潜在的解决方案是保持代码的原样,并在println()正在写入的控制台上使用单间距字体,但是这种方法仍然存在一些主要问题。首先,您需要确定一个字体是单间距的,但也将支持您想要呈现的所有字符。当包含表情符号时,这可能会有问题。第二,即使您识别了这样的字体,您可能会发现,该字体的所有符号都不是单间距的!这样的字体将确保(比方说)小写i和大写W具有相同的宽度,但您也不能对表情符号进行这种假设,甚至不能假设“单频”表情符号都具有相同的非标准宽度!第三,您标识的字体(如果存在的话)必须在目标环境(您的PowerShell、朋友的PuTTY外壳等)中可用。这不是一个主要的障碍,但这是另一件值得担心的事情。
  • 您可能会发现呈现的文本因操作系统而异。您的输出可能在Linux终端窗口中看起来对齐,但使用相同字体的相同输出在PowerShell窗口中可能出现对齐。

在所有这些情况下,更好的方法可能是使用Swing或JavaFX,在这里您可以更好地控制正在呈现的输出。即使您不熟悉这些技术,也不需要太长时间就可以工作,只需调整一些通过搜索获得的示例代码即可。即使考虑到学习曲线,也比想出一个健壮的解决方案来对写到任意控制台的任意字符要花费更少的时间,因为这是一个很难解决的问题。

备注:

票数 1
EN

Stack Overflow用户

发布于 2022-02-26 02:29:26

注意:这个答案与我以前的回答不同,在质量上也不同(我仍然支持这个答案)。

Java应用程序(即不使用图形用户界面的应用程序)有一种简单的方法来获得在给定字体大小的给定字体中呈现的字符串的宽度。它需要使用一些awt类,即使在非AWT环境中也支持这些类。下面是一个使用问题中提供的数据的演示:

代码语言:javascript
复制
package fixedwidth;

import java.awt.Canvas;
import java.awt.Font;
import java.awt.FontMetrics;

public class FixedWidth {

    static String[] tests = {
        "Peter", "SHGAMI", "Marcel №1", "", "‍❤️‍", "‍❤️‍‍", "‍‍"
    };
    static Font smallFont = new Font("Monospaced", Font.PLAIN, 10);
    static Font bigFont = new Font("Monospaced", Font.BOLD, 24);

    /**
     * This code is based on an answer by SO user Lonzak. 
     * See SO Answer https://stackoverflow.com/a/18123024/2985643
     */
    public static void main(String[] args) {
        FontMetrics fm1 = new Canvas().getFontMetrics(FixedWidth.smallFont);
        FixedWidth.demo(tests, fm1);

        FontMetrics fm2 = new Canvas().getFontMetrics(FixedWidth.bigFont);
        FixedWidth.demo(tests, fm2);
    }

    static void demo(String[] tests, FontMetrics fm) {
        Font f = fm.getFont();
        System.out.println("\nFont name:" + f.getName() + ", font size:" + 
                f.getSize() + ", font style:" + f.getStyle());
        for (String test : tests) {
            int width = fm.stringWidth(test);
            System.out.println("width=" + width + ", data=" + test);
        }
    }
}

上面的代码是基于这是Lonzak用户的旧答案的问题没有图形的Java - FontMetrics。这些AWT类允许您创建一个具有定义特征(即名称、大小、样式)的Font,然后在使用该字体时使用FontMetrics实例获取任意字符串的宽度。

下面是运行上面所示代码的输出:

代码语言:javascript
复制
Font name:Monospaced, font size:10, font style:0
width=30, data=Peter
width=60, data=SHGAMI
width=59, data=Marcel №1
width=10, data=
width=30, data=‍❤️‍
width=40, data=‍❤️‍‍
width=30, data=‍‍

Font name:Monospaced, font size:24, font style:1
width=70, data=Peter
width=149, data=SHGAMI
width=140, data=Marcel №1
width=25, data=
width=73, data=‍❤️‍
width=98, data=‍❤️‍‍
width=74, data=‍‍

备注:

  • 第一组结果显示了当使用普通单间距10点字体时,问题中样本数据的宽度。第二组结果显示了使用粗体、单间距24点字体时相同字符串的宽度。
  • 对于某些表情符号,宽度看起来并不正确,但这是因为当源代码和输出结果被粘贴到其中时,一些表情符号表示被改变了,这大概是因为浏览器中使用的字体不同。(我对源和输出都使用了单频标。)下面是原始输出的屏幕截图,显示宽度至少看起来是可信的:
  • 尽管宽度是为固定宽度字体(单步字体)计算和呈现的,但很明显,不能从普通键盘字符的宽度来预测表情符号的宽度。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71228251

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档