8

我一直在使用 Java 的 BufferedWriter 写入文件以解析一些输入。但是,当我打开文件后,似乎添加了空字符。我尝试将编码指定为“US-ASCII”和“UTF8”,但得到了相同的结果。这是我的代码片段:

Scanner fileScanner = new Scanner(original);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "US-ASCII"));
while(fileScanner.hasNextLine())
  {
     String next = fileScanner.nextLine();
     next = next.replaceAll(".*\\x0C", ""); //remove up to ^L
     out.write(next);
     out.newLine();
  }
 out.flush();
 out.close();

也许问题甚至不在于 BufferedWriter?

我已将其缩小到此代码块,因为如果我将其注释掉,则输出文件中没有空字符。如果我在 VIM 中进行正则表达式替换,则文件不含空字符 (:%s/.*^L//g)。

如果您需要更多信息,请与我们联系。

谢谢!

编辑: 普通行的 hexdump 看起来像:0000000 5349 2a41 3030 202a

但是当这段代码运行时,hexdump 看起来像:0000000 5330 2a49 4130 202a

我不确定为什么事情会变得混乱。

编辑: 此外,即使文件与正则表达式不匹配并运行该代码块,它也会出现空字符。

编辑: 这是 diff 前几行的 hexdump:http: //pastie.org/pastes/8964701/text

命令是:diff -y testfile.hexdump expectedoutput.hexdump

其余的行与最后两行不同。

4

4 回答 4

9

编辑:查看您给出的 hexdump 差异,唯一的区别是一个有 LF 行结尾(0A),另一个有 CRLF 行结尾(0D 0A)。diff 中的所有其他数据都向前移动以容纳额外的字节。

CRLF 是在您使用的操作系统上结束的默认行。如果您想要以输出结尾的特定行,请写入字符串"\n""\r\n".

以前我注意到Scanner没有指定字符集。它应该指定输入已知被编码的适当的那个。但是,这不是意外输出的来源。

于 2014-03-24T15:07:52.183 回答
0

Stuart Caie 给出了答案。如果您正在寻找避免这些字符的代码。

基本问题是,Org 文件使用不同的行分隔符和新文件使用不同的行分隔符。

一种简单的方法是找到 Org 文件分隔符并在新文件中使用它。

    try(BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            Scanner fileScanner = new Scanner(original);) {
        String lineSep = null;
        boolean lineSepFound = false;
        while(fileScanner.hasNextLine())
        {

            if (!lineSepFound){
                MatchResult matchResult = fileScanner.match();
                if (matchResult != null){
                    lineSep = matchResult.group(1);
                    if (lineSep != null){
                        lineSepFound = true;
                    }
                }
            }else{
                out.write(lineSep);
            }
            String next = fileScanner.nextLine();
            next = next.replaceAll(".*\\x0C", ""); //remove up to ^L
            out.write(next);

        }
    } catch ( IOException e) {
        e.printStackTrace();
    }

注意 **MatchResult matchResult = fileScanner.match();将为最后执行的匹配提供 matchResult。在我们的例子中,我们使用了 hasNextLine() - Scanner 使用 linePattern 来查找下一行.. Scanner.hasNextLine源代码查找行分隔符,

但不幸的是,没有办法恢复行分隔符。所以我只用了他们的代码来获取 lineSep 一次。并使用该 lineSep 创建新文件。

同样根据您的代码,您将在文件末尾有额外的行分隔符。在这里更正。

让我知道这是否有效。

于 2014-03-26T17:23:18.183 回答
0

我认为发生了以下情况

包含 ^L (ff) 的所有行都被修改以删除 ^L 之前的所有内容,但此外,您在 1 中的副作用是所有 \r (cr) 也被删除。但是,如果 cr 出现在 ^L nextLine() 之前,也将其视为一行。请注意,在下面的输出文件中,输入文件中 cr + nl 的数量是 6,而 cr + nl 的数量也是 6,但它们都是 nl,因此带有 c 的行被保留,因为它正在被处理与 ^L 不同的行。可能不是你想要的。见下文。

一些观察

  1. 源文件正在使用 \r\n 定义新行的系统上生成,而您的程序正在不使用的系统上运行。因此,所有出现的 0xd 都将被删除。即使没有^L,这也会使两个文件大小不同。

  2. 但是您可能忽略了 #1,因为 vim 将在 DOS 模式下运行(将 \r\n 识别为换行符)或非 DOS 模式(仅 \n),这取决于它在打开文件时读取的内容并隐藏了事实用户如果可以的话。事实上,为了测试,我不得不在 \r 中使用 ^v^m 进行暴力破解,因为我在 Linux 上使用 vim more here进行编辑。

  3. 您的测试方法可能是使用 od -x (十六进制正确)?但这输出的整数不是你想要的。考虑以下输入文件和输出文件。在你的程序运行之后。在 vi 中查看

输入文件

a
b^M
c^M^M ^L
d^L

输出文件

a
b
c

好吧,也许这是对的,让我们看看 od 要说什么

输入文件的 od -x

0a61    0d62    630a    0d0d    0c20    640a    0a0c 

输出文件的 od -x

0a61    0a62    0a63    0a0a    000a

咦,那个null是从哪里来的?但是从 od 的手册页等待

-t type     Specify the output format.  type is a string containing one or more of the following kinds of type specifiers:

   q          a       Named characters (ASCII).  Control characters are displayed using the following names:
-h, -x      Output hexadecimal shorts.  Equivalent to -t x2.
-a          Output named characters.  Equivalent to -t a.

哦,好的,所以改用 -a 选项

od -a 输入

a  nl   b  cr  nl   c  cr  cr  sp  ff  nl   d  ff  nl

od -a 输出

a  nl   b  nl   c  nl  nl  nl  nl 

强制 java 忽略 \r

最后,话虽如此,您确实必须克服对 java 的隐含理解,即 \r 分隔一行,甚至与文档相反。即使明确设置扫描仪使用忽略模式,它仍然与文档相反,您必须通过设置分隔符再次覆盖它(见下文)。我发现以下内容可能会通过坚持 Unix 行语义来满足您的要求。我还添加了一些逻辑以不输出空行。

public static void repl(File original,File file) throws IOException
{
   Scanner fileScanner = new Scanner(original);
   Pattern pattern1 = Pattern.compile("(?d).*");

   fileScanner.useDelimiter("(?d)\\n");

   BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"));

   while(fileScanner.hasNext(pattern1))
   {
      String next = fileScanner.next(pattern1);

      next = next.replaceAll("(?d)(.*\\x0C)|(\\x0D)","");
      if(next.length() != 0)
      {
         out.write(next);
         out.newLine();
      }
   }
   out.flush();
   out.close();
}

随着这一变化,上面的输出变为。

od -a 输入

a  nl   b  cr  nl   c  cr  cr  sp  ff  nl   d  ff  nl

od -a 输出

a  nl   b  nl
于 2014-03-24T19:41:46.430 回答
0

Scanner.nextLine() 正在吃现有的行尾。nextLine
的javadoc指出:

此方法返回当前行的其余部分,不包括末尾的任何行分隔符。

BufferedWriter.newLine的javadoc解释说:

写入行分隔符。行分隔符字符串由系统属性 line.separator 定义,不一定是单个换行符 ('\n')。

在您的情况下,您系统的默认换行符是“\n”。您正在解析的 EDI 文件使用“\r\n”。

在这种情况下,不适合使用系统定义的 newLine 分隔符。要使用的换行符由文件格式决定,并且应该放在某个格式特定的静态常量中。

更改“out.newLine();” 到 "out.write("\r\n");"

于 2014-03-24T18:57:01.260 回答