在 Java 中对齐文本

IT小君   2022-01-08T04:35:31

我必须读入一个整数,这将是后续行的长度。(文本行永远不会超过提供的长度)。

然后我必须阅读每一行文本并将空格尽可能均匀地转换为下划线。例如:

我会输入 30 的行长。然后是一行文本Hello this is a test string然后,所有的空间都将被转换为下划线,并填补了,从而使文本填充像这样给定的线路长度:Hello__this__is__a_test_string如您所见,原始文本的长度为 27 个字符,因此要将其填充到 30 个字符,我必须在原始文本中添加 3 个额外的空格,然后将这些空格转换为下划线字符。

请你能建议我可以解决这个问题的方法吗?

评论(9)
IT小君

我所做的是将句子分成单词。然后计算出需要添加多少空格。然后遍历单词并为每个单词添加一个空格,直到您用完可以添加的空格。如果您有足够的空格需要在单词中添加多个单词(例如您有 5 个单词,但需要添加 13 个空格),只需将剩余空格数除以单词数,然后将该数字添加到每个单词先说一句话​​。然后你可以取剩余部分并遍历添加空格的单词,直到你完成。还要确保只在句子中除最后一个单词之外的所有单词中添加空格。

2022-01-08T04:35:32   回复
IT小君

我最近不得不在 Java 中做类似的事情。代码本身相对简单。我发现花了最长的时间,是让我了解证明过程。

我开始逐步说明如何手动证明文本的合理性。

  1. 看看这条线有多长
  2. 找出该行上的字符串有多长
  3. 计算添加到字符串以等于行长所需的空格数
  4. 找出字符串中的单词之间有多少间隙
  5. 计算要添加到字符串中每个间隙的空格数
  6. 将结果添加到每个间隙
  7. 计算有多少额外的空格要连续添加到每个间隙(如果间隙数不能被要添加的空格数整除。例如,如果您有 5 个间隙但要添加 6 个空格)
  8. 在间隙中添加额外的空格
  9. 将空格转换为下划线
  10. 返回字符串

这样做使我对算法的编码变得更加简单!

找出线和所述线上的字符串有多长

你说你已经阅读了行长和行上的文字,所以 1 和 2 你已经完成了。2 是一个简单的string.length()调用。

计算添加到字符串以等于行长度所需的空格数只是取行长度并减去字符串的长度。

lineLength - string.length() = noofspacestoadd;

找出字符串中所有单词之间有多少间隙

可能有不止一种方法可以做到这一点。我发现执行此操作的最简单方法是将字符串转换为 char[],然后遍历检查 ' ' 的字符并设置一个计数,以便它确实找到一个 ' '

计算每个间隙要添加多少个空格

这是一个简单的除法计算!

noofgaps / noofspacestoadd = noofspacestoaddtoeachgap;

注意:你必须确保你用整数做这个除法!由于 5 / 2 = 2.5,因此您知道您必须在单词之间的每个间隙中添加 2 个空格,并且使用 int 的除法会截断十进制数以形成整数。

将结果添加到每个间隙

在能够添加添加到每个间隙所需的字符串数量之前,您需要将此数字转换为一串空格。因此,您需要编写一个方法,将给定整数转换为与该给定数字相等的空格字符串。同样,这可以通过不同的方式来完成。我这样做的方式是这样的

String s = "";
for(int i=noofspacestoaddtoeachgap; i>0; i--)
{
    s+= " ";
}

return s;

我这样做的方法是将字符串转换为子字符串数组,子字符串是数组中的每个单词。如果您在 javadoc 中查找 String 类,您应该会在 String 类中找到可用于实现此目的的方法!

当您拥有子字符串数组时,您可以将空格字符串添加到每个子字符串的末尾以形成新的子字符串!

计算有多少额外的空间

这又是一个简单的计算。使用 % 运算符,您可以进行类似于我们之前进行的除法的余数除法。

noofgaps % noofspacestoadd = noofspacestoaddtoeachgap;

该计算的结果为我们提供了对齐文本所需的额外空格数。

将额外的空格连续添加到每个间隙

这可能是算法中最困难的部分,因为您必须找出一种方法来遍历单词之间的每个间隙并添加额外的空格,直到没有多余的空格可以添加!

返回字符串

return String;
2022-01-08T04:35:32   回复
IT小君

您只需要调用需要fullJustify()在单词列表中传递的方法以及您想要输出的每行的最大宽度。

public List<String> fullJustify(String[] words, int maxWidth) {
    int n = words.length;
    List<String> justifiedText = new ArrayList<>();
    int currLineIndex = 0;
    int nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
    while (currLineIndex < n) {
        StringBuilder line = new StringBuilder();
        for (int i = currLineIndex; i < nextLineIndex; i++) {
            line.append(words[i] + " ");
        }
        currLineIndex = nextLineIndex;
        nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
        justifiedText.add(line.toString());
    }
    for (int i = 0; i < justifiedText.size() - 1; i++) {
        String fullJustifiedLine = getFullJustifiedString(justifiedText.get(i).trim(), maxWidth);
        justifiedText.remove(i);
        justifiedText.add(i, fullJustifiedLine);
    }
    String leftJustifiedLine = getLeftJustifiedLine(justifiedText.get(justifiedText.size() - 1).trim(), maxWidth);
    justifiedText.remove(justifiedText.size() - 1);
    justifiedText.add(leftJustifiedLine);
    return justifiedText;
}

public static int getNextLineIndex(int currLineIndex, int maxWidth, String[] words) {
    int n = words.length;
    int width = 0;
    while (currLineIndex < n && width < maxWidth) {
        width += words[currLineIndex++].length() + 1;
    }
    if (width > maxWidth + 1)
        currLineIndex--;
    return currLineIndex;
}

public String getFullJustifiedString(String line, int maxWidth) {
    StringBuilder justifiedLine = new StringBuilder();
    String[] words = line.split(" ");
    int occupiedCharLength = 0;
    for (String word : words) {
        occupiedCharLength += word.length();
    }
    int remainingSpace = maxWidth - occupiedCharLength;
    int spaceForEachWordSeparation = words.length > 1 ? remainingSpace / (words.length - 1) : remainingSpace;
    int extraSpace = remainingSpace - spaceForEachWordSeparation * (words.length - 1);
    for (int j = 0; j < words.length - 1; j++) {
        justifiedLine.append(words[j]);
        for (int i = 0; i < spaceForEachWordSeparation; i++)
            justifiedLine.append(" ");
        if (extraSpace > 0) {
            justifiedLine.append(" ");
            extraSpace--;
        }
    }
    justifiedLine.append(words[words.length - 1]);
    for (int i = 0; i < extraSpace; i++)
        justifiedLine.append(" ");
    return justifiedLine.toString();
}

public String getLeftJustifiedLine(String line, int maxWidth) {
    int lineWidth = line.length();
    StringBuilder justifiedLine = new StringBuilder(line);
    for (int i = 0; i < maxWidth - lineWidth; i++)
        justifiedLine.append(" ");
    return justifiedLine.toString();
}

以下是 maxWidth 为80 个字符的示例转换: 以下段落正好包含115 个单词将转换后的文本写入外部文件需要55毫秒

我已经用大约70k+的段落测试了这段代码,将转换后的文本写入文件大约需要400毫秒

输入

这些特征往往使法律写作正式。这种形式可以采取长句子、复杂结构、过时和超形式词汇的形式,以及对内容的关注而排除读者的需求。鉴于某些法律文件的重要性以及使用某些法律文件的情况的严重性,法律书面形式中的某些形式是必要和可取的。然而,并非法律文书中的所有形式都是合理的。在某种程度上,形式会产生不透明和不精确,这是不可取的。在某种程度上,形式阻碍了读者的理解,它是不太可取的。特别是,当必须将法律内容传达给非律师时,形式应该让位于清晰的沟通。

输出

These  features  tend  to make legal writing formal. This formality can take the
form   of  long  sentences,  complex  constructions,  archaic  and  hyper-formal
vocabulary,  and  a  focus  on content to the exclusion of reader needs. Some of
this formality in legal writing is necessary and desirable, given the importance
of  some  legal documents and the seriousness of the circumstances in which some
legal  documents  are used. Yet not all formality in legal writing is justified.
To   the   extent  that  formality  produces  opacity  and  imprecision,  it  is
undesirable.  To  the  extent that formality hinders reader comprehension, it is
less   desirable.  In  particular,  when  legal  content  must  be  conveyed  to
nonlawyers, formality should give way to clear communication.                   
2022-01-08T04:35:32   回复
IT小君

让我们尝试分解问题:

从 30 中减去字符串的长度 - 这就是您将在某处添加的额外空格的数量(在本例中为 3)。

计算现有空间的数量(在本例中为 5)。

现在您知道您需要将第一批额外空间尽可能均匀地分配到现有空间中(在这种情况下,将 3 分配到 5 中)。

想想你会如何在现实生活中分配这样的东西,比如将球放入桶中。你可能会在你的桶中旋转,在每个桶里放一个球,直到你用完为止。因此,请考虑如何在您的 java 代码中实现这一点(提示:查看不同类型的循环)。

2022-01-08T04:35:32   回复
IT小君

我要解决的方法是使用带有正则表达式替换的循环。

  1. 用下划线替换所有空格。
  2. 对于使长度达到所需长度所需的每个字符,将单个下划线替换为两个下划线。使用正则表达式来确保这些替换只发生在不存在所需数量的下划线的情况下。请参阅JavaDoc 了解 .ReplaceFirst()您还需要考虑必须用三元组替换双下划线的可能性。

完成初始替换后,我建议您使用 while 循环,以字符串长度小于目标大小为限。int numUnderscores = 1;在 while 之外初始化那么循环内的步骤将是:

  1. 建立替换模式。这应该类似于"/[^_](_{" + numUnderscores + "})[^_]/"“任何不是下划线的字符,后跟下划线字符的 numUnderscores 实例,然后是不是下划线的任何字符”
  2. 致电.ReplaceFirst()执行更换
  3. 检查字符串是否包含当前下划线数量的任何剩余实例;如果没有,那么你必须增加numUnderscores

显然,由于这是一个家庭作业问题,因此我将编写代码的实际过程留作练习。如果您对其中的某些部分或我描述的逻辑结构的某些组件有具体问题,请在评论中提问!

这样做的好处是它适用于任何大小的字符串,并且可以针对不同的情况进行配置。

2022-01-08T04:35:33   回复
IT小君

这个问题最难的事情是定义“尽可能均匀”。

你的例子:

 Hello__this__is__a_test_string

... 使所有较长的间隙都在左侧。不会:

 Hello__this_is__a_test__string

...更适合问题的不精确描述,较长的间隙均匀分布在输出字符串中?

但是,让我们解决它,以便它给出示例答案。

  • 首先你需要知道你需要插入多少额外的字符—— numNewChars==lengthWanted减号inputString.length()
  • 接下来,您需要计算在它们之间分配这些新字符的间隔有多少——称之为numGaps——它是单词数减一。
  • 在每个空格中,您将插入一个nn+1新的空格。nnumNewChars / numGaps——整数除法;四舍五入。
  • 现在,您需要插入多少次n+1新空格而不是n? 这是剩下的:plusOnes = numNewChars % numGaps

这就是你需要的所有数字。现在使用你学过的任何方法(因为这显然是一个家庭作业问题,你不想使用你的课程中没有涉及的语言特性或库),遍历字符串:

  • 对于第一个plusOnes空格,n+1除了已经存在的空格外,还要插入空格。
  • 对于剩余的空格,插入n空格。

一种非常基本的方法如下:

String output= "";
for(int i=0; i<input.length(); i++) {
    char c = input.charAt(i);
    if(c == ' ' {
        output += ...; // appropriate number of "_" chars
    } else {
        output += "" + c; // "" + just turns the char into a String.
    }
}
2022-01-08T04:35:33   回复
IT小君

我遵循了 Shahroz Saleem 的回答(但我的代表太低,无法发表评论:/) - 但是,我需要做一个小改动,因为它没有考虑超过行长的单词(例如文本中的 URL。)

import java.util.ArrayList;
import java.util.List;

public class Utils {

    public static List<String> fullJustify(String words, int maxWidth) {

        return fullJustify(words.split(" "), maxWidth);
    }

    public static List<String> fullJustify(String[] words, int maxWidth) {
        int n = words.length;
        List<String> justifiedText = new ArrayList<>();
        int currLineIndex = 0;
        int nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
        while (currLineIndex < n) {
            StringBuilder line = new StringBuilder();
            for (int i = currLineIndex; i < nextLineIndex; i++) {
                line.append(words[i] + " ");
            }
            currLineIndex = nextLineIndex;
            nextLineIndex = getNextLineIndex(currLineIndex, maxWidth, words);
            justifiedText.add(line.toString());
        }
        for (int i = 0; i < justifiedText.size() - 1; i++) {
            String fullJustifiedLine = getFullJustifiedString(justifiedText.get(i).trim(), maxWidth);
            justifiedText.remove(i);
            justifiedText.add(i, fullJustifiedLine);
        }
        String leftJustifiedLine = getLeftJustifiedLine(justifiedText.get(justifiedText.size() - 1).trim(), maxWidth);
        justifiedText.remove(justifiedText.size() - 1);
        justifiedText.add(leftJustifiedLine);
        return justifiedText;
    }

    public static int getNextLineIndex(int currLineIndex, int maxWidth, String[] words) {
        int n = words.length;
        int width = 0;
        int count = 0;
        while (currLineIndex < n && width < maxWidth) {
            width += words[currLineIndex++].length() + 1;
            count++;
        }
        if (width > maxWidth + 1 && count > 1)
            currLineIndex--;

        return currLineIndex;
    }

    public static String getFullJustifiedString(String line, int maxWidth) {
        StringBuilder justifiedLine = new StringBuilder();
        String[] words = line.split(" ");
        int occupiedCharLength = 0;
        for (String word : words) {
            occupiedCharLength += word.length();
        }
        int remainingSpace = maxWidth - occupiedCharLength;
        int spaceForEachWordSeparation = words.length > 1 ? remainingSpace / (words.length - 1) : remainingSpace;
        int extraSpace = remainingSpace - spaceForEachWordSeparation * (words.length - 1);
        for (int j = 0; j < words.length - 1; j++) {
            justifiedLine.append(words[j]);
            for (int i = 0; i < spaceForEachWordSeparation; i++)
                justifiedLine.append(" ");
            if (extraSpace > 0) {
                justifiedLine.append(" ");
                extraSpace--;
            }
        }
        justifiedLine.append(words[words.length - 1]);
        for (int i = 0; i < extraSpace; i++)
            justifiedLine.append(" ");
        return justifiedLine.toString();
    }

    public static String getLeftJustifiedLine(String line, int maxWidth) {
        int lineWidth = line.length();
        StringBuilder justifiedLine = new StringBuilder(line);
        //for (int i = 0; i < maxWidth - lineWidth; i++)
        //    justifiedLine.append(" ");
        return justifiedLine.toString();
    }
}

请注意,我还注释掉了每个段落最后一行的空格填充(在 getLeftJustifiedLine 中),并使方法静态..

2022-01-08T04:35:33   回复
IT小君

本演示文稿的第一部分包含文本对齐的动态规划算法。

2022-01-08T04:35:33   回复
IT小君

我写了一个简单的方法来证明文本。它不是 100% 准确,但在大多数情况下都有效(因为它完全忽略了标点符号,并且可能也缺少一些边缘情况)。此外,Word 以更丰富的方式对齐文本(通过不添加空格来填充空白,而是均匀分布空白的宽度,这在此处很难做到)。

public static void justifyText (String text) {
    int STR_LENGTH = 80;
    int end=STR_LENGTH, extraSpacesPerWord=0, spillOverSpace=0;
    String[] words;

    System.out.println("Original Text: \n" + text);
    System.out.println("Justified Text: ");

    while(end < text.length()) {

        if(text.charAt(STR_LENGTH) == ' ') {
            // Technically, this block is redundant
            System.out.println (text.substring(0, STR_LENGTH));
            text = text.substring(STR_LENGTH);
            continue;
        }

        end = text.lastIndexOf(" ", STR_LENGTH);
        words = text.substring(0, end).split(" ");
        extraSpacesPerWord = (STR_LENGTH - end) / words.length;
        spillOverSpace = STR_LENGTH - end + (extraSpacesPerWord * words.length);

        for(String word: words) {
            System.out.print(word + " ");
            System.out.print((extraSpacesPerWord-- > 0) ? " ": "");
            System.out.print((spillOverSpace-- > 0) ? " ": "");
        }
        System.out.print("\n");
        text = text.substring(end+1);

    }
    System.out.println(text);

}
2022-01-08T04:35:34   回复