0

Shift-JIS でエンコードされた XML ファイルを読み込んで ByteBuffer に格納し、それを文字列に変換して、Pattern & Matcher で文字列の開始と終了を見つけようとしています。これらの 2 つの位置から、バッファをファイルに書き込もうとします。マルチバイト文字がない場合に機能します。マルチバイト文字がある場合、 end の値が少しずれているため、末尾のテキストが欠落しています

static final Pattern startPattern = Pattern.compile("<\\?xml ");
static final Pattern endPattern = Pattern.compile("</doc>\n");

 public static void main(String[] args) throws Exception {
    File f = new File("20121114000606JA.xml");
    FileInputStream fis = new FileInputStream(f);
    FileChannel fci = fis.getChannel();
    ByteBuffer data_buffer = ByteBuffer.allocate(65536);
    while (true) {
      int read = fci.read(data_buffer);
      if (read == -1)
        break;
    }

    ByteBuffer cbytes = data_buffer.duplicate();
    cbytes.flip();
    Charset data_charset = Charset.forName("UTF-8");
    String request = data_charset.decode(cbytes).toString();

    Matcher start = startPattern.matcher(request);
    if (start.find()) {
      Matcher end = endPattern.matcher(request);

      if (end.find()) {

        int i0 = start.start();
        int i1 = end.end();

        String str = request.substring(i0, i1);

        String filename = "test.xml";
        FileChannel fc = new FileOutputStream(new File(filename), false).getChannel();

        data_buffer.position(i0);
        data_buffer.limit(i1 - i0);

        long offset = fc.position();
        long sz = fc.write(data_buffer);

        fc.close();
      }
    }
    System.out.println("OK");
  }
4

3 に答える 3

1

次のバイト位置に文字列インデックスi0 および i1 を使用します。

data_buffer.position(i0);
data_buffer.limit(i1 - i0);

は誤りです。UTF-8 は一意のエンコーディングを提供しないため、ĉ2 つの文字c+ ダイアクリティカル マークを組み合わせて記述されるため^、文字とバイトの間の相互変換はコストがかかるだけでなく、エラーが発生しやすくなります (特定のデータの rand の場合)。

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
        new File(filename)), "UTF-8"));

または、CharSequence を実装する CharBuffer を使用します。


FileChannel fc に書き込む代わりに:

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
        new File(filename)), "UTF-8"));
try {
    out.write(str);
} finally {
    out.close();
}

CharBuffer バージョンは、パターン マッチングにも触れて、さらに書き直す必要があります。

于 2012-11-15T15:14:28.543 に答える
0

このファイルを適切にトランスコードするには、Java の XML API を使用する必要があります。これを行うにはいくつかの方法がありますが、ここでは javax.xml.transform パッケージを使用したソリューションを示します。まず、ドキュメントで参照されている djnml-1.0b.dtd ファイルが本当に必要です (エンティティ参照が含まれている場合)。これがないため、このソリューションでは、Trangを使用して、提供された入力から生成された DTD を使用します。

<?xml encoding="UTF-8"?>

<!ELEMENT doc (djnml)>
<!ATTLIST doc
  xmlns CDATA #FIXED ''
  destination NMTOKEN #REQUIRED
  distId NMTOKEN #REQUIRED
  md5 CDATA #REQUIRED
  msize CDATA #REQUIRED
  sysId NMTOKEN #REQUIRED
  transmission-date NMTOKEN #REQUIRED>

<!ELEMENT djnml (head,body)>
<!ATTLIST djnml
  xmlns CDATA #FIXED ''
  docdate CDATA #REQUIRED
  product NMTOKEN #REQUIRED
  publisher NMTOKEN #REQUIRED
  seq CDATA #REQUIRED
  xml:lang NMTOKEN #REQUIRED>

<!ELEMENT head (copyright,docdata)>
<!ATTLIST head
  xmlns CDATA #FIXED ''>

<!ELEMENT body (headline,text)>
<!ATTLIST body
  xmlns CDATA #FIXED ''>

<!ELEMENT copyright EMPTY>
<!ATTLIST copyright
  xmlns CDATA #FIXED ''
  holder CDATA #REQUIRED
  year CDATA #REQUIRED>

<!ELEMENT docdata (djn)>
<!ATTLIST docdata
  xmlns CDATA #FIXED ''>

<!ELEMENT headline (#PCDATA)>
<!ATTLIST headline
  xmlns CDATA #FIXED ''
  brand-display NMTOKEN #REQUIRED
  prefix CDATA #REQUIRED>

<!ELEMENT text (pre,p+)>
<!ATTLIST text
  xmlns CDATA #FIXED ''>

<!ELEMENT djn (djn-newswires)>
<!ATTLIST djn
  xmlns CDATA #FIXED ''>

<!ELEMENT pre EMPTY>
<!ATTLIST pre
  xmlns CDATA #FIXED ''>

<!ELEMENT p (#PCDATA)>
<!ATTLIST p
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-newswires (djn-press-cutout,djn-urgency,djn-mdata)>
<!ATTLIST djn-newswires
  xmlns CDATA #FIXED ''
  news-source NMTOKEN #REQUIRED
  origin NMTOKEN #REQUIRED
  service-id NMTOKEN #REQUIRED>

<!ELEMENT djn-press-cutout EMPTY>
<!ATTLIST djn-press-cutout
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-urgency (#PCDATA)>
<!ATTLIST djn-urgency
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-mdata (djn-coding)>
<!ATTLIST djn-mdata
  xmlns CDATA #FIXED ''
  accession-number CDATA #REQUIRED
  brand NMTOKEN #REQUIRED
  display-date NMTOKEN #REQUIRED
  hot NMTOKEN #REQUIRED
  original-source NMTOKEN #REQUIRED
  page-citation CDATA #REQUIRED
  retention NMTOKEN #REQUIRED
  temp-perm NMTOKEN #REQUIRED>

<!ELEMENT djn-coding (djn-company,djn-isin,djn-industry,djn-subject,
                      djn-market,djn-product,djn-geo)>
<!ATTLIST djn-coding
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-company (c)>
<!ATTLIST djn-company
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-isin (c)>
<!ATTLIST djn-isin
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-industry (c)+>
<!ATTLIST djn-industry
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-subject (c)+>
<!ATTLIST djn-subject
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-market (c)+>
<!ATTLIST djn-market
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-product (c)+>
<!ATTLIST djn-product
  xmlns CDATA #FIXED ''>

<!ELEMENT djn-geo (c)+>
<!ATTLIST djn-geo
  xmlns CDATA #FIXED ''>

<!ELEMENT c (#PCDATA)>
<!ATTLIST c
  xmlns CDATA #FIXED ''>

このファイルを「djnml-1.0b.dtd」に書き出した後、XSLT を使用して恒等変換を作成する必要があります。TransformerFactory の newTransformer() メソッドでこれを行うことができますが、この変換の結果は明確に指定されていません。XSLT を使用すると、よりクリーンな結果が得られます。このファイルを恒等変換として使用します。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

上記の XSLT ファイルを「identity.xsl」として保存します。DTD と恒等変換ができたので、次のコードを使用してファイルをトランスコードできます。

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

...

File inFile = new File("20121114000606JA.xml");
File outputFile = new File("test.xml");
final File dtdFile = new File("djnml-1.0b.dtd");
File identityFile = new File("identity.xsl");

final List<Closeable> closeables = new ArrayList<Closeable>();
try {
  // We are going to use a SAXSource for input, so that we can specify the
  // location of the DTD with an EntityResolver.
  InputStream in = new FileInputStream(inFile);
  closeables.add(in);
  InputSource fileSource = new InputSource();
  fileSource.setByteStream(in);
  fileSource.setSystemId(inFile.toURI().toString());

  SAXSource source = new SAXSource();
  XMLReader reader = XMLReaderFactory.createXMLReader();
  reader.setEntityResolver(new EntityResolver() {
    public InputSource resolveEntity(String publicId, String systemId)
        throws SAXException, IOException {
      if (systemId != null && systemId.endsWith("/djnml-1.0b.dtd")) {
        InputStream dtdIn = new FileInputStream(dtdFile);
        closeables.add(dtdIn);

        InputSource inputSource = new InputSource();
        inputSource.setByteStream(dtdIn);
        inputSource.setEncoding("UTF-8");

        return inputSource;
      }
      return null;
    }
  });

  source.setXMLReader(reader);
  source.setInputSource(fileSource);

  // Now we need to create a StreamResult.
  OutputStream out = new FileOutputStream(outputFile);
  closeables.add(out);
  StreamResult result = new StreamResult();
  result.setOutputStream(out);
  result.setSystemId(outputFile);

  // Create a templates object for the identity transform.  If you are going
  // to transform a lot of documents, you should do this once and
  // reuse the Templates object.
  InputStream identityIn = new FileInputStream(identityFile);
  closeables.add(identityIn);
  StreamSource identitySource = new StreamSource();
  identitySource.setSystemId(identityFile);
  identitySource.setInputStream(identityIn);
  TransformerFactory factory = TransformerFactory.newInstance();
  Templates templates = factory.newTemplates(identitySource);

  // Finally we need to create the transformer and do the transformation.
  Transformer transformer = templates.newTransformer();
  transformer.transform(source, result);

} finally {
  // Some older XML processors are bad at cleaning up input and output streams,
  // so we will do this manually.
  for (Closeable closeable : closeables) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (Exception e) {
      }
    }
  }
}
于 2012-11-16T10:07:04.683 に答える
0

ここでの問題は、バイトバッファのデコードにあるようです。UTF-8 CharSet を使用して Shift-JIS ByteBuffer をデコードしています。これを Shift-JIS CharSet に変更する必要があります。サポートされている文字エンコーディングは次のとおりです。

テストする Shift-JIS ファイルはありませんが、CharSet.forName 行を次のように変更してみてください。

Charset data_charset = Charset.forName("Shift_JIS");

また、正規表現ロジックが少しずれています。2 番目のマッチャーは使用しません。これにより、検索が最初からやり直され、逆の範囲になる可能性があるためです。代わりに、現在の一致の位置を取得してから、マッチャーが使用しているパターンを変更してみてください。

Matcher matcher = startPattern.matcher(request);
if (matcher.find()) {
  int i0 = matcher.start();
  matcher.usePattern(endPattern);

  if (matcher.find()) {

    int i1 = matcher.end();

Shift-JIS は2 バイトのエンコーディング システムであるため、Java UTF-8 文字にきれいにマッピングする必要があります。これにより、これを「START.*END」のような単一のパターンと一致させ、グループを使用してデータを取得できるようになります。

于 2012-11-15T17:54:45.700 に答える