Изменение записи в двоичном файле с помощью Perl - PullRequest
0 голосов
/ 07 июня 2018

Привет,

У меня есть XML-файл, который мне нужно проверить.Для этого я использую следующий код

use strict;
use warnings;
use XML::Parser;

my $File="folder/file1.xml";

my $p1 = new XML::Parser();
my $p2;
my $Crash_Error_String='';

eval{$p2=$p1->parsefile($File)};
$Crash_Error_String=$@ if !defined $p2 ;
if(!defined $p2){
    print $Crash_Error_String . "\n";
}

Теперь, если файл не содержит действительный XML, я получаю строку в переменной $ Crash_Error_String следующим образом:

не правильно сформирован (недопустимый токен) в строке 1771, столбец 58, байт 248467 в строке /usr/lib64/perl5/XML/Parser.pm 187.

Это говорит о том, что существует проблема, связанная с XML, в файле в байте 248467

Теперь я могу распечатать значение, с которым возникает проблема:

my($fh, $File, $byte_position, $byte_value);

$byte_position = 248467;

open($fh, "+<", $File) || die "can't open $File: $!";

binmode($fh) || die "can't binmode $File";

sysseek($fh, $byte_position, 0)  # NB: 0-based
      || die "couldn't see to byte $byte_position in $File: $!";

sysread($fh, $byte_value, 1) == 1
      || die "couldn't read byte from $File: $!";

printf "read byte with ordinal value %#02x at position %d\n",
    ord($byte_value), $byte_position;

close $fh;

, которое в этом конкретном примере дает

считанный байт с порядковым значением 0x1f в позиции248467

Теперь для моей проблемы: как я могу заменить значение 0x1f на запись _x001f_

Я попробовал следующее (поместив код ниже между вызовами «sysread» и «close»в приведенном выше коде)

sysseek($fh, $byte_position, 0)  # NB: 0-based
      || die "couldn't see to byte $byte_position in $File: $!";

my $NewV="_x001f_";

syswrite($fh,$NewV);

Но это сразу помещает новое значение справа от строки проблемы.Кроме того, он пожирает символы справа.

Итак, перед ошибкой у меня есть следующий фрагмент в файле (символ, на который жалуется XML Parser, на самом деле не показан ниже, но в основном это символ междуя и е из vérifier) ​​

для проверки

И после замены у меня есть следующий фрагмент в файле

для vérifi_x001f_éaction

Как вы можете видеть, строка замены сгорела в следующей части строки.

Я хочу заменить:

pour vérifi_x001f_er la réaction

Любая помощь очень ценится.

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Если файл слишком большой для памяти, но дисковое пространство не является проблемой, самое простое решение:

  1. Создайте новый файл.
  2. Скопируйте первый 248467байт в новый файл.
  3. Распечатать последовательность замены в новый файл.
  4. Считать один байт из старого файла.
  5. Скопировать оставшиеся байты в новый файл.

Это можно сделать на месте, но это намного сложнее (и проблема приведет к потере данных).

use Fcntl qw( SEEK_CUR SEEK_SET );

use constant BLOCK_SIZE => 4*1024*1024;

my $qfn = 'file';
my $offset = 248467;

open(my $fh_src, '<:raw',  $qfn) or die("Can't open \"$qfn\": $!\n");
open(my $fh_dst, '+<:raw', $qfn) or die("Can't open \"$qfn\": $!\n");

sysseek($fh_src, $offest, SEEK_SET) or die($!);
sysseek($fh_dst, $offest, SEEK_SET) or die($!);

my $buf;
{
   my $rv = sysread($fh_src, $buf, 1);
   die($!) if !defined($rv);
   die("Premature EOF") if !$rv;
   # Since we're only reading one byte, we don't need to worry about a partial read.
   $buf = sprintf("_x%04x_", ord($buf));
}

while (1) {
   my $written = 0;
   while ($written < length($buf)) {
      my $rv = syswrite($fh_dst, $buf, length($buf)-$written, $written);
      die($!) if !defined($rv);
      $written += $rv;
   }

   my $rv = sysread($fh_src, $buf, BLOCK_SIZE);
   die($!) if !defined($rv);
   last if !$rv;
}

# Must use sysseek instead of tell with sysread/syswrite.    
truncate($fh_dst, sysseek($fh_dst, 0, SEEK_CUR))
   or die($!);

Технически, truncate isn 'Требуется, потому что новый файл всегда будет больше старого.

0 голосов
/ 07 июня 2018
› perl -i.bak -0777 -lpe's/\x1f/_x001f_/g' 50738935.xml

› hex 50738935.xml.bak
0000  70 6f 75 72 20 76 c3 a9  72 69 66 69 1f 65 72 20  pour v.. rifi.er 
0010  6c 61 20 72 c3 a9 61 63  74 69 6f 6e 0a           la r..ac tion.

› hex 50738935.xml
0000  70 6f 75 72 20 76 c3 a9  72 69 66 69 5f 78 30 30  pour v.. rifi_x00
0010  31 66 5f 65 72 20 6c 61  20 72 c3 a9 61 63 74 69  1f_er la  r..acti
0020  6f 6e 0a                                          on.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...