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 にもありませんか?

このコード ブロックに絞り込んだのは、コメント アウトした場合、出力ファイルに null 文字が含まれないためです。VIM で正規表現置換を行うと、ファイルにヌル文字がなくなります (:%s/.*^L//g)。

さらに情報が必要な場合はお知らせください。

ありがとう!

編集: 通常の行の hexdump は次のようになります: 0000000 5349 2a41 3030 202a

しかし、このコードを実行すると、hexdump は次のようになります: 0000000 5330 2a49 4130 202a

なぜ物事が混同されているのかわかりません。

編集: また、ファイルが正規表現と一致せず、そのコードブロックを実行しても、null 文字が表示されます。

編集: これは、差分の最初の数行の 16 進ダンプです: http://pastie.org/pastes/8964701/text

コマンドは次のとおりです: diff -y testfile.hexdump expectedoutput.hexdump

残りの行は、最後の 2 つのように異なります。

4

4 に答える 4

9

編集:あなたが与えたhexdump diffを見ると、唯一の違いは、一方にはLF行末(0A)があり、もう一方にはCRLF行末(0D 0A)があることです。差分の他のすべてのデータは、余分なバイトに対応するために前方にシフトされます。

CRLF は、使用している OS で終わるデフォルトの行です。出力で特定の行の終わりが必要な場合は、文字列"\n"または"\r\n".

前に、Scannerは文字セットを指定しないことに注意しました。入力がエンコードされていることがわかっている適切なものを指定する必要があります。ただし、これは予期しない出力の原因ではありません。

于 2014-03-24T15:07:52.183 に答える
0

何が起こっているかは次のとおりだと思います

^L (ff) を含むすべての行は、^L の前のすべてを削除するように変更されますが、さらに、すべての \r (cr) も削除されるという副作用が 1 にあります。ただし、cr が ^L の前に表示される場合、nextLine() はそれも行として扱います。以下の出力ファイルでは、入力ファイルの cr + nl の数が 6 であり、cr + nl の数も 6 ですが、それらはすべて nl であることに注意してください。したがって、c を含む行は処理されているため保持されます。 ^L とは異なる行です。おそらくあなたが望むものではありません。下記参照。

いくつかの観察

  1. \r\n を使用して新しい行を定義するシステムでソース ファイルが生成されており、それを使用しないシステムでプログラムが実行されています。このため、出現する 0xd はすべて削除されます。これにより、^L がない場合でも、2 つのファイルのサイズが異なります。

  2. しかし、おそらく #1 を見落としていると思われます。これは、vim がファイルを開くときに読み取る内容に応じて、DOS モード (\r\n を改行区切りとして認識する) または非 DOS モード (\n のみ) で動作するためです。できればユーザー。実際、テストするには、 Linux で vim more hereを使用して編集していたため、^v^m を使用して \r でブルート フォースする必要がありました。

  3. テストする手段は、おそらく od -x (16 進数の場合) を使用することですか? しかし、それはあなたが望むものではないintを出力します。次の入力ファイルと出力ファイルを考えてみましょう。プログラムの実行後。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

え、そのヌルはどこから来たの?しかし、odのmanページから待ってください

-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

そして最後に、ドキュメントに反していても、 \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 に答える
0

Stuart Caie が答えを提供してくれました。これらの文字を避けるコードを探している場合。

基本的な問題は、異なる行区切り文字を使用する Org ファイルと、異なる行区切り文字を使用する新しいファイルです。

簡単な方法の 1 つは、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();は、最後に実行された Match の matchResult を提供します。そして私たちの場合、 hasNextLine() を使用しました - Scanner は linePattern を使用して次の行を見つけました.. Scanner.hasNextLineソースコードは行 Separator を見つけました。

残念ながら、行区切りを元に戻す方法はありません。だから私は彼らのコードを使って lineSep を一度だけ取得しました。そのlineSepを使用して新しいファイルを作成しました。

また、コードごとに、ファイルの最後に余分な行区切りがあります。ここで修正。

それがうまくいくかどうか教えてください。

于 2014-03-26T17:23:18.183 に答える