【问题标题】:Justify text in Java在 Java 中对齐文本
【发布时间】:2012-01-21 10:30:01
【问题描述】:

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

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

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

请您建议我可以解决此问题的方法吗?

【问题讨论】:

  • 你试过什么?这有没有给你带来麻烦的方面?
  • 我可以读取行长的整数,以及每行的文本(字符串),我可以将空格替换为下划线。但是我不确定如何确保我均匀地添加额外的空间。
  • 当你说“均匀”时,你想要它有多均匀?我的意思是,在上面的Hello__th... 示例中,您可以将其拉平:所有添加的空格都在左侧,如果您将“__”从“this”和“is”之间移动到“test”之间和“字符串”你会得到一些看起来更“均匀”的东西。我认为这种水平的均匀度是没有必要的——你只关心当至少一对单词之间有一个下划线时,没有两个单词之间有 3 个下划线。

标签: java string replace


【解决方案1】:

让我们试着分解问题:

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

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

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

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

【讨论】:

  • 我曾考虑将每个空格添加到一个数组中,但在尝试对数组进行排序时,我发现数组有点混乱。会不会像 "while string
【解决方案2】:

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

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

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

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

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

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

【讨论】:

  • 此解决方案的额外好处是您也可以将其用作代码拼图 :)
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    this presentation 的第一部分包含文本对齐的动态规划算法。

    【讨论】:

      【解决方案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;
      

      【讨论】:

      • 谢谢!我很感激
      【解决方案6】:

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

      你的例子:

       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.
          }
      }
      

      【讨论】:

        【解决方案7】:

        我写了一个简单的方法来证明文本。它不是 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);
        
        }
        

        【讨论】:

        • 经过测试,但有问题。打破大量的测试用例。不要使用!
        【解决方案8】:

        您只需要调用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.                   
        

        【讨论】:

          【解决方案9】:

          我遵循了 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 中),并使方法静态..

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-01-28
            • 2011-12-26
            • 1970-01-01
            • 2012-09-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多