6

我有一个头文件,其中有一个很大的结构。我需要使用一些程序读取这个结构并对结构的每个成员进行一些操作并将它们写回。

例如,我有一些结构,如

const BYTE Some_Idx[] = {
4,7,10,15,17,19,24,29,
31,32,35,45,49,51,52,54,
55,58,60,64,65,66,67,69,
70,72,76,77,81,82,83,85,
88,93,94,95,97,99,102,103,
105,106,113,115,122,124,125,126,
129,131,137,139,140,149,151,152,
153,155,158,159,160,163,165,169,
174,175,181,182,183,189,190,193,
197,201,204,206,208,210,211,212,
213,214,215,217,218,219,220,223,
225,228,230,234,236,237,240,241,
242,247,249};

现在,我需要阅读这篇文章并对每个成员变量应用一些操作,并创建一个具有不同顺序的新结构,例如:

const BYTE Some_Idx_Mod_mul_2[] = {
8,14,20, ...
...
484,494,498};

是否有任何可用的 Perl 库?如果不是 Perl,其他类似 Python 的东西也可以。

有人可以帮忙吗!!!

4

9 回答 9

9

将数据放在标题中会使使用 Perl 等其他程序变得更加棘手。您可能考虑的另一种方法是将这些数据保存在数据库或另一个文件中,并根据需要重新生成头文件,甚至可能作为构建系统的一部分。这样做的原因是生成 C 比解析 C 容易得多,编写一个解析文本文件并为您生成标题的脚本很简单,甚至可以从您的构建系统中调用这样的脚本。

假设您要将数据保存在 C 头文件中,您将需要以下两种方法之一来解决此问题:

  • 一个快速的一次性脚本,用于准确(或接近准确地)解析您描述的输入。
  • 一个通用的,编写良好的脚本,可以解析任意 C 并且通常可以处理许多不同的标头。

第一种情况对我来说似乎比第二种情况更常见,但很难从你的问题中看出这是否可以通过需要解析任意 C 的脚本或需要解析此特定文件的脚本更好地解决。对于适用于您的特定情况的代码,以下内容适用于您的输入:

#!/usr/bin/perl -w

use strict;

open FILE, "<header.h" or die $!;
my @file = <FILE>;
close FILE or die $!;

my $in_block = 0;
my $regex = 'Some_Idx\[\]';
my $byte_line = '';
my @byte_entries;
foreach my $line (@file) {
    chomp $line;

    if ( $line =~ /$regex.*\{(.*)/ ) {
        $in_block = 1;
        my @digits = @{ match_digits($1) };
        push @digits, @byte_entries;
        next;
    }

    if ( $in_block ) {
        my @digits = @{ match_digits($line) };
        push @byte_entries, @digits;
    }

    if ( $line =~ /\}/ ) {
        $in_block = 0;
    }
}

print "const BYTE Some_Idx_Mod_mul_2[] = {\n";
print join ",", map { $_ * 2 } @byte_entries;
print "};\n";

sub match_digits {
    my $text = shift;
    my @digits;
    while ( $text =~ /(\d+),*/g ) {
        push @digits, $1;
    }

    return \@digits;
}

解析任意 C 有点棘手,对于许多应用程序来说并不值得,但也许您需要实际执行此操作。一个技巧是让 GCC 为您进行解析并使用名为GCC::TranslationUnit的 CPAN 模块读取 GCC 的解析树。这是编译代码的 GCC 命令,假设您有一个名为 test.c 的文件:

gcc -fdump-translation-unit -c test.c

这是要在解析树中读取的 Perl 代码:

  use GCC::TranslationUnit;

  # echo '#include <stdio.h>' > stdio.c
  # gcc -fdump-translation-unit -c stdio.c
  $node = GCC::TranslationUnit::Parser->parsefile('stdio.c.tu')->root;

  # list every function/variable name
  while($node) {
    if($node->isa('GCC::Node::function_decl') or
       $node->isa('GCC::Node::var_decl')) {
      printf "%s declared in %s\n",
        $node->name->identifier, $node->source;
    }
  } continue {
    $node = $node->chain;
  }
于 2009-06-15T07:05:21.430 回答
6

对不起,如果这是一个愚蠢的问题,但为什么要担心解析文件呢?为什么不编写一个#include 标头的 C 程序,根据需要对其进行处理,然后吐出修改后的标头的源代码。我确信这会比 Perl/Python 解决方案更简单,而且它会更可靠,因为头文件将由 C 编译器解析器解析。

于 2009-06-15T08:08:37.783 回答
4

您并没有真正提供有关如何确定要修改的内容的太多信息,而是针对您的具体示例:

$ perl -pi.bak -we'if ( /const BYTE Some_Idx/ .. /;/ ) { s/Some_Idx/Some_Idx_Mod_mul_2/g; s/(\d+)/$1 * 2/ge; }' header.h

打破它, -p 表示循环输入文件,将每一行放入$_,运行提供的代码,然后打印$_。-i.bak 启用就地编辑,使用 .bak 后缀重命名每个原始文件并打印到以原始文件命名的新文件。-w 启用警告。-e'....' 提供要为每个输入行运行的代码。header.h 是唯一的输入文件。

在 perl 代码中,if ( /const BYTE Some_Idx/ .. /;/ )检查我们是否处于以 line matching 开头并以 line matching/const BYTE Some_Idx/结尾的行范围内/;/。s/.../.../g 尽可能多地进行替换。 /(\d+)/匹配一系列数字。/e 标志表示结果 ( $1 * 2) 是应该被评估以产生替换字符串的代码,而不是简单的替换字符串。$1 是应该替换的数字。

于 2009-06-15T06:47:14.327 回答
3

如果您只需要修改结构,您可以直接使用正则表达式拆分并应用更改到结构中的每个值,寻找声明和结束 }; 知道什么时候停止。

如果您真的需要更通用的解决方案,您可以使用解析器生成器,例如PyParsing

于 2009-06-15T06:44:49.043 回答
2

有一个名为Parse::RecDescent的 Perl 模块,它是一个非常强大的递归下降解析器生成器。它带有一堆示例。其中之一是可以解析 C 的语法

现在,我认为这在您的情况下并不重要,但是使用 Parse::RecDescent 的递归下降解析器在算法上比 Parse::Yapp 或 Parse::EYapp 等工具在算法上要慢(我认为是 O(n^ 2 ) 。我没有检查 Parse::EYapp 是否带有这样的 C 解析器示例,但如果是这样,那是我建议学习的工具。

于 2009-06-15T07:47:45.023 回答
2

Python 解决方案(不完整,只是一个提示;))抱歉,如果有任何错误 - 未经测试

import re
text = open('your file.c').read()
patt = r'(?is)(.*?{)(.*?)(}\s*;)'
m = re.search(patt, text)
g1, g2, g3 = m.group(1), m.group(2), m.group(3)
g2 = [int(i) * 2 for i in g2.split(',')
out = open('your file 2.c', 'w')
out.write(g1, ','.join(g2), g3)
out.close()
于 2009-06-15T07:59:22.920 回答
2

有一个非常有用的 Perl 模块,称为Convert::Binary::C,它解析 C 头文件并将结构从/转换为 Perl 数据结构。

于 2009-06-15T14:02:05.937 回答
0

您始终可以使用pack/unpack来读取和写入数据。

#! /usr/bin/env perl
use strict;
use warnings;
use autodie;

my @data;
{
  open( my $file, '<', 'Some_Idx.bin' );

  local $/ = \1; # read one byte at a time

  while( my $byte = <$file> ){
    push @data, unpack('C',$byte);
  }
  close( $file );
}

print join(',', @data), "\n";

{
  open( my $file, '>', 'Some_Idx_Mod_mul_2.bin' );

  # You have two options
  for my $byte( @data ){
    print $file pack 'C', $byte * 2;
  }
  # or
  print $file pack 'C*', map { $_ * 2 } @data;

  close( $file );
}
于 2009-06-15T19:15:51.343 回答
0

对于 GCC::TranslationUnit 示例,请参见http://gist.github.com/395160 中的 hparse.pl,它将使其成为 C::DynaLib,以及尚未编写的 Ctypes。这会解析 FFI 的函数,而不是与 Convert::Binary::C 相反的裸结构。如果用作 func args,hparse 只会添加结构。

于 2010-05-10T16:29:44.787 回答