在Java中对齐文本

3
我需要读取一个整数,它将成为接下来的文本行的长度。(文本行的长度永远不会超过提供的长度)。
之后我需要读取每一行文本,并尽可能均匀地将其中的空格转换为下划线。例如:
我输入了30作为行长度,然后输入了一行文本"Hello this is a test string"。然后所有的空格都将被转换为下划线,并进行填充,使文本填满给定的行长度,像这样:Hello__this__is__a_test_string。如您所见,原始文本长度为27个字符,所以为了将其填充到30个字符,我不得不向原始文本添加3个额外的空格,然后将这些空格转换为下划线字符。
请问我该如何处理这个问题?

你尝试过什么?有哪个方面让你感到困难吗? - stew
我可以读取每行长度的整数和每行的文本(字符串),并将空格替换为下划线。但是,我不确定如何确保均匀地添加额外的空格。 - Michael
当你说“均匀”时,你想要多均匀呢?我的意思是,在上面的Hello__th...示例中,你甚至可以更加均匀:所有添加的空格都在左边,如果你将“__”从“this”和“is”之间移动到“test”和“string”之间,你会得到一个看起来更加“均匀”的东西。我想你并不需要这种程度的均匀性——你只关心是否存在至少一对有一个下划线的单词对,它们之间没有3个下划线。 - flies
9个回答

6
我所做的是将句子分成单词。然后计算需要添加多少个空格。接着遍历每个单词并在每个单词后面添加一个空格,直到没有空格可添加为止。如果你需要在单词之间添加超过一个空格(比如你有5个单词,但需要添加13个空格),只需将剩余的空格数除以单词数,然后将这个数字先加到每个单词上。然后你可以取余数,并遍历每个单词,在每个单词后面添加一个空格,直到完成。同时,请确保只在句子中除了最后一个单词之外的所有单词后面添加空格。

5

最近我在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;

3
你只需要调用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();
}

以下是最大宽度为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.                   

1

让我们试着将问题分解:

从30中减去字符串的长度 - 这就是你需要添加的额外空格数(在这种情况下为3)。

计算现有空格的数量(在这种情况下为5)。

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

想象一下在现实生活中如何分配类似的东西,比如把球放进桶里。你可能会轮流使用桶,每个桶放一个球,直到用完为止。因此,请考虑如何在你的Java代码中实现这一点(提示:查看不同类型的循环)。


我考虑将每个空格添加到数组中,但是当尝试对它们进行排序时,我发现数组有点令人困惑。类似于“while string < line length string.charAt(string.replace(" ", "_"))”这样的东西是否可行? - Michael

1

我会使用带有正则表达式替换的循环来完成这个任务。

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

在进行初始替换后,建议使用while循环,其边界是字符串长度小于目标大小。在while之外初始化int numUnderscores = 1;。然后循环内的步骤将是:

  1. 构建替换模式。这应该是像“/ [^ _] (_ {”+ numUnderscores +“}) [^ _] /”这样的东西,它表示“不是下划线的任何字符,后跟numUnderscores个下划线字符,后跟不是下划线的任何字符”
  2. 调用.ReplaceFirst()来执行替换
  3. 检查字符串中是否包含当前数量的下划线的任何剩余实例; 如果没有,则必须增加numUnderscores

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

这种方法的好处是它适用于任何大小的字符串,并且非常可配置以适应不同的情况。


这个解决方案的附加好处是,你也可以把它当作一个代码谜题来使用 :) - Laurent Grégoire

1
这个问题最难的地方在于定义“尽可能均匀”的含义。
你的例子:
 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.
    }
}

1

我按照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中),并将方法设置为静态的。

0

本演示的第一部分涉及到一个动态规划算法,用于文本的对齐。


0
我编写了一个简单的文本对齐方法。它并不完全准确,但大部分情况下都可用(因为它完全忽略了标点符号,某些特殊情形可能会被忽略)。同时,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);

}

已测试,但有漏洞。在许多测试用例中出现故障。请勿使用! - Laurent Grégoire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接