4

学術雑誌の記事を含む多数のテキスト ファイル (1000 以上) があります。残念ながら、各記事のファイルには、前の記事の終わり (最初) と次の記事の最初 (最後) の「スタブ」も含まれています。

スタブは重複データを構成するため、記事の頻度分析を実行する準備として、これらのスタブを削除する必要があります。

すべての場合において、各記事の開始と終了を示す単純なフィールドはありません。ただし、重複したテキストは、どちらの場合も同じ行に同じ形式で表示されているようです。

各ファイルを次のファイルと比較し、重複テキストの 1 つのコピーを削除するスクリプトは完璧です。これは、プログラミングの際にはかなり一般的な問題のように思われるので、これを行うものを見つけることができなかったことに驚いています.

ファイル名は順番にソートされるため、各ファイルを次のファイルと順番に比較するスクリプトが機能するはずです。例えば

bul_9_5_181.txt
bul_9_5_186.txt

181 ページから始まる記事と 186 ページから始まる記事の 2 つの記事があります。これらの記事はどちらも以下に含まれています。

[ http://drop.io/fdsayre][1]に 2 つのボリュームのテスト データがあります。

注: 私は、心理学の歴史におけるプロジェクトの古いジャーナル記事の内容分析を行っている学者です。私はプログラマーではありませんが、Linux で 10 年以上の経験があり、通常は作業を進めていくうちに理解できます。

ご協力いただきありがとうございます

ファイル名: bul_9_5_181.txt

シンセシア

ISI

ポルトガル語の単語の大部分は、黒い物または黒に関連する考えを意味します。確かに、この関連性は真の共感覚ではありませんが、著者は、これらの論理的かつ自発的な関連性と、有色オーディションの真のケースとの間の程度の問題にすぎないと考えています。参考文献

DOWNEY, JUNE E. 着色された味覚の事例。アメール。J. of Psycho!., 1911, 22, S28-539MEDEIROS-E-ALBUQUERQUE. Sur un phenomene de synopsie presente par des millions de sujets. / . デサイコ。ノルムらの道、1911年、8、147-151。MYERS、CS 共感覚のケース。ブリット。J. of Psychol.、1911、4、228-238。

感情現象 — ミシガン大学ジョン F. シェパード教授による実験

ライプツィヒの研究所からは、1 年の間に 3 つの論文が発表されました。Drozynski (2) は、呼吸障害が関与している可能性があるため、感情を伴う有機的反応の研究に味覚刺激と嗅覚刺激を使用することに反対しています。彼はリズミカルな聴覚刺激を使用し、さまざまな速度でさまざまなグループに分けて与えられると、各被験者に特徴的な感情が伴うことを発見しました. 彼は胸の呼吸を記録し、血圧計と脈波計から曲線を描きます。各実験は通常の記録から始まり、次に刺激が与えられ、その後にコントラスト刺激が続きました。最後に、別の法線が取られました。呼吸の長さと深さを測定し(タイムラインは記録されていません)、呼気の長さと吸気の長さの関係を決定しました。脈拍の長さと高さも測定されました。表形式の要約は、著者が各タイプの感情の反応期間中に各量が増加または減少したことを発見した回数を示しています。与えられたリズムに伴う感情状態は常に複雑ですが、その結果は支配的であると思われる次元に言及されます。記録から再現されているのは、正常期と反応期から切り離されたわずかな抜粋のみです。著者は、興奮が呼吸の速度と深さ、吸気と呼気の比率、および脈拍の速度とサイズを増加させると述べています. アームボリュームにうねりがあります。効果が静かである限り、速度と深さの減少を引き起こします。表形式の要約は、著者が各タイプの感情の反応期間中に各量が増加または減少したことを発見した回数を示しています。与えられたリズムに伴う感情状態は常に複雑ですが、その結果は支配的であると思われる次元に言及されます。記録から再現されているのは、正常期と反応期から切り離されたわずかな抜粋のみです。著者は、興奮が呼吸の速度と深さ、吸気と呼気の比率、および脈拍の速度とサイズを増加させると述べています. アームボリュームにうねりがあります。効果が静かである限り、速度と深さの減少を引き起こします。表形式の要約は、著者が各タイプの感情の反応期間中に各量が増加または減少したことを発見した回数を示しています。与えられたリズムに伴う感情状態は常に複雑ですが、その結果は支配的であると思われる次元に言及されます。記録から再現されているのは、正常期と反応期から切り離されたわずかな抜粋のみです。著者は、興奮が呼吸の速度と深さ、吸気と呼気の比率、および脈拍の速度とサイズを増加させると述べています. アームボリュームにうねりがあります。効果が静かである限り、速度と深さの減少を引き起こします。しかし、その結果は、支配的であると思われる次元に言及されます。記録から再現されているのは、正常期と反応期から切り離されたわずかな抜粋のみです。著者は、興奮が呼吸の速度と深さ、吸気と呼気の比率、および脈拍の速度とサイズを増加させると述べています. アームボリュームにうねりがあります。効果が静かである限り、速度と深さの減少を引き起こします。しかし、その結果は、支配的であると思われる次元に言及されます。記録から再現されているのは、正常期と反応期から切り離されたわずかな抜粋のみです。著者は、興奮が呼吸の速度と深さ、吸気と呼気の比率、および脈拍の速度とサイズを増加させると述べています. アームボリュームにうねりがあります。効果が静かである限り、速度と深さの減少を引き起こします。

182

ジョン・F・シェパード

呼吸、吸気呼気比、脈拍数とサイズ。アームボリュームは、呼吸波とともに上昇する傾向を示します。協調性が示す

4

7 に答える 7

4

はるかに単純なソリューションが実際に機能するようです。

ファイル名によって提供される情報を誰も使用していないようです。この情報を利用すれば、重複領域を特定するためにファイル間の比較を行う必要がない場合があります。OCR を作成した人は誰でも、おそらくこの問題について考えたことでしょう。

ファイル名の最後の数字は、そのファイルの開始ページ番号を示しています。このページ番号は、ファイル内でも 1 行に表示されます。また、この行の前後に空白行があるようです。したがって、特定のファイルについて、シーケンス内の次のファイルの名前を調べて、テキストの削除を開始するページ番号を決定できるはずです。このページ番号はファイルに表示されるため、この番号のみを含む行 (前後に空白行がある行) を探し、その行以降をすべて削除します。シーケンスの最後のファイルはそのままにしておくことができます。

アルゴリズムの概要は次のとおりです

  1. ファイルを選択します。それを呼び出します: file1
  2. 次のファイルのファイル名を見てください。それを呼んでください:file2
  3. file2 のファイル名からページ番号を抽出します。それを呼び出します: ページ番号
  4. pageNumber のみを含む行が見つかるまで、file1 の内容をスキャンします。
  5. この行の前後に空白行があることを確認してください。
  6. この行とその後のすべてを削除します
  7. シーケンス内の次のファイルに移動する
于 2009-04-15T20:44:04.513 に答える
3

おそらく次のようなことを試す必要があります(提供されたサンプルデータでテストしました):

#!/usr/bin/ruby

class A_splitter
    Title   = /^[A-Z]+[^a-z]*$/
    Byline  = /^BY /
    Number = /^\d*$/
    Blank_line = /^ *$/
    attr_accessor :recent_lines,:in_references,:source_glob,:destination_path,:seen_in_last_file
    def initialize(src_glob,dst_path=nil)
        @recent_lines = []
        @seen_in_last_file = {}
        @in_references = false
        @source_glob = src_glob
        @destination_path = dst_path
        @destination = STDOUT
        @buffer = []
        split_em
        end
    def split_here
        if destination_path
            @destination.close if @destination
            @destination = nil
          else
            print "------------SPLIT HERE------------\n" 
          end
        print recent_lines.shift
        @in_references = false
        end
    def at_page_break
        ((recent_lines[0] =~ Title  and recent_lines[1] =~ Blank_line and recent_lines[2] =~ Number) or
         (recent_lines[0] =~ Number and recent_lines[1] =~ Blank_line and recent_lines[2] =~ Title))
        end
    def print(*args)
        (@destination || @buffer) << args
        end
    def split_em
        Dir.glob(source_glob).sort.each { |filename|
            if destination_path
                @destination.close if @destination
                @destination = File.open(File.join(@destination_path,filename),'w')
                print @buffer
                @buffer.clear
              end
            in_header = true
            File.foreach(filename) { |line|
                line.gsub!(/\f/,'')
                if in_header and seen_in_last_file[line]
                    #skip it
                  else 
                    seen_in_last_file.clear if in_header
                    in_header = false
                    recent_lines << line
                    seen_in_last_file[line] = true
                  end
                3.times {recent_lines.shift} if at_page_break
                if recent_lines[0] =~ Title and recent_lines[1] =~ Byline
                    split_here
                  elsif in_references and recent_lines[0] =~ Title and recent_lines[0] !~ /\d/
                    split_here
                  elsif recent_lines.length > 4
                    @in_references ||= recent_lines[0] =~ /^REFERENCES *$/
                    print recent_lines.shift
                  end
                }
            } 
        print recent_lines
        @destination.close if @destination
        end
    end

A_splitter.new('bul_*_*_*.txt','test_dir')

基本的に、ファイルを順番に実行し、各ファイル内で行を順番に実行し、各ファイルから前のファイルに存在していた行を省略し、残りを STDOUT (そこからパイプできる) に出力します。 director が指定されている場合 (例では「test_dir」と呼ばれる最後の行を参照)、ファイルは指定されたディレクトリーに、その内容の大部分を含むファイルと同じ名前で作成されます。

また、改ページ セクション (雑誌のタイトル、著者、ページ番号) も削除します。

2 つの分割テストを行います。

  • タイトル/バイライン ペアのテスト
  • 参照セクションの後の最初のタイトル行のテスト

(追加の分割ポイントのテストを追加する方法は明らかです)。

後世のために保持:

宛先ディレクトリを指定しない場合、分割ポイントの出力ストリームに単にここに分割行が配置されます。これにより、テストが容易になり(less出力のみを実行できます)、個々のファイルにそれらをパイプするだけですcsplit(たとえば、

csplit -f abstracts - '---SPLIT HERE---' '{*}'

または何か)それを切ります。

于 2009-04-09T17:34:31.290 に答える
2

些細な問題があります。ファイル1の終わりとファイル2の始まりで重複するテキストを見つけるコードを書くのは簡単です。しかし、重複するテキストを削除したくない--- 2番目の記事の始まりでそれを分割したいです。分割を正しく行うのは難しいかもしれません---1つのマーカーはすべて大文字で、もう1つはBY次の行の先頭にあります。

連続したファイルからの例があれば助かりますが、以下のスクリプトは1つのテストケースで機能します。このコードを試す前に、すべてのファイルをバックアップしてください。 コードは既存のファイルを上書きします。

実装はLuaにあります。アルゴリズムは大まかに次のとおりです。

  1. ファイル1の終わりとファイル2の始まりの空白行は無視してください。
  2. ファイル1の終わりとファイル2の始まりに共通する長い行のシーケンスを見つけます。
    • これは、40行、次に39行のシーケンスを試すことで機能します。
  3. 両方のファイルからシーケンスを削除し、それを呼び出しますoverlap
  4. タイトルでオーバーラップを分割
  5. オーバーラップの最初の部分をfile1に追加します。file2の2番目の部分を付加します。
  6. ファイルの内容を行のリストで上書きします。

コードは次のとおりです。

#!/usr/bin/env lua

local ext = arg[1] == '-xxx' and '.xxx' or ''
if #ext > 0 then table.remove(arg, 1) end  

local function lines(filename)
  local l = { }
  for line in io.lines(filename) do table.insert(l, (line:gsub('', ''))) end
  assert(#l > 0, "No lines in file " .. filename)
  return l
end

local function write_lines(filename, lines)
  local f = assert(io.open(filename .. ext, 'w'))
  for i = 1, #lines do
    f:write(lines[i], '\n')
  end
  f:close()
end

local function lines_match(line1, line2)
  io.stderr:write(string.format("%q ==? %q\n", line1, line2))
  return line1 == line2 -- could do an approximate match here
end

local function lines_overlap(l1, l2, k)
  if k > #l2 or k > #l1 then return false end
  io.stderr:write('*** k = ', k, '\n')
  for i = 1, k do
    if not lines_match(l2[i], l1[#l1 - k + i]) then
      if i > 1 then
        io.stderr:write('After ', i-1, ' matches: FAILED <====\n')
      end
      return false
    end
  end
  return true
end

function find_overlaps(fname1, fname2)
  local l1, l2 = lines(fname1), lines(fname2)
  -- strip trailing and leading blank lines
  while l1[#l1]:find '^[%s]*$' do table.remove(l1)    end
  while l2[1]  :find '^[%s]*$' do table.remove(l2, 1) end
  local matchsize  -- # of lines at end of file 1 that are equal to the same 
                   -- # at the start of file 2
  for k = math.min(40, #l1, #l2), 1, -1 do
    if lines_overlap(l1, l2, k) then
      matchsize = k
      io.stderr:write('Found match of ', k, ' lines\n')
      break
    end
  end

  if matchsize == nil then
    return false -- failed to find an overlap
  else
    local overlap = { }
    for j = 1, matchsize do
      table.remove(l1) -- remove line from first set
      table.insert(overlap, table.remove(l2, 1))
    end
    return l1, overlap, l2
  end
end

local function split_overlap(l)
  for i = 1, #l-1 do
    if l[i]:match '%u' and not l[i]:match '%l' then -- has caps but no lowers
      -- io.stderr:write('Looking for byline following ', l[i], '\n')
      if l[i+1]:match '^%s*BY%s' then
        local first = {}
        for j = 1, i-1 do
          table.insert(first, table.remove(l, 1))
        end
        -- io.stderr:write('Split with first line at ', l[1], '\n')
        return first, l
      end
    end
  end
end

local function strip_overlaps(filename1, filename2)
  local l1, overlap, l2 = find_overlaps(filename1, filename2)
  if not l1 then
    io.stderr:write('No overlap in ', filename1, ' an
于 2009-04-07T01:59:35.090 に答える
2

これは、Perl で考えられる別のソリューションの始まりです (このままでも機能しますが、必要に応じてより洗練されたものになる可能性があります)。あなたが懸念しているのは、コーパス全体で重複を削除することだけであり、どこにも重複していない限り、ある記事の最後の部分が次の記事のファイルにあるかどうかはあまり気にしないように思えます。その場合、このソリューションは重複する行を取り除き、一連のファイル全体で特定の行のコピーを 1 つだけ残します。

テキスト ファイルを含むディレクトリ内のファイルを引数なしで実行するか、処理するファイルのリストを含むファイル名を指定して、処理する順序でファイルを実行することができます。コマンドラインでlsのような単純なコマンドを使用したり、Perl スクリプトでglobを使用したりすると、ファイル名 (少なくとも提供されたサンプルファイルでは) が自然に順番にリストされないため、後者をお勧めします。したがって、( glob コマンドによって入力または生成された) リストをたどっていくだけなので、必ずしも正しいファイルを互いに比較するとは限りません。リストを指定すると、それらが正しい順序で処理され、適切に設定するのにそれほど時間がかからないことを保証できます。

このスクリプトは単純に 2 つのファイルを開き、2 番目のファイルの最初の 3 行をメモします。次に、最初のファイルの新しい出力ファイル (元のファイル名 + '.new') を開き、2 番目のファイルの最初の 3 行が見つかるまで、最初のファイルのすべての行を新しい出力ファイルに書き出します。最後のファイルの 2 番目のファイルからの 3 行がない可能性がありますが、ジャーナル名のヘッダーとページ番号のために、私が確認したすべてのファイルにそのように思われました。ジャーナルのタイトルが最初の行であることが多く、それでは物事が早く途切れてしまうため、1 行では絶対に十分ではありませんでした。

また、入力されたファイルのリストの最後のファイルは、このプロセスによって変更されないため、処理されない (つまり、それに基づいて新しいファイルが作成される) ことにも注意してください。

スクリプトは次のとおりです。

#!/usr/bin/perl
use strict;

my @files;
my $count = @ARGV;
if ($count>0){
    open (IN, "$ARGV[0]");
    @files = <IN>;
    close (IN);
} else {
    @files = glob "bul_*.txt";
}
$count = @files;
print "Processing $count files.\n";

my $lastFile="";
foreach(@files){
    if ($lastFile ne ""){
        print "Processing $_\n";
        open (FILEB,"$_");
        my @fileBLines = <FILEB>;
        close (FILEB);
        my $line0 = $fileBLines[0];
            if ($line0 =~ /\(/ || $line0 =~ /\)/){
                    $line0 =~ s/\(/\\\(/;
                    $line0 =~ s/\)/\\\)/;
            }
        my $line1 = $fileBLines[1];
        my $line2 = $fileBLines[2];
        open (FILEA,"$lastFile");
        my @fileALines = <FILEA>;
        close (FILEA);
        my $newName = "$lastFile.new";
        open (OUT, ">$newName");
        my $i=0;
        my $done = 0;
        while ($done != 1 and $i < @fileALines){
            if ($fileALines[$i] =~ /$line0/ 
                && $fileALines[$i+1] == $line1
                && $fileALines[$i+2] == $line2) {
                $done=1;
            } else {
                print OUT $fileALines[$i];
                $i++;
            }
        }
        close (OUT);
    }
    $lastFile = $_;
}

編集:最初の行に括弧のチェックを追加しました。これは後で重複の正規表現チェックに入り、見つかった場合はそれらをエスケープして、重複チェックを台無しにしないようにします。

于 2009-04-15T01:28:26.257 に答える
0

スタブが両方のファイルで厳密に同一であると仮定して、簡単に突き刺します。

#!/usr/bin/perl

use strict;

use List::MoreUtils qw/ indexes all pairwise /;

my @files = @ARGV;

my @previous_text;

for my $filename ( @files ) {
    open my $in_fh,  '<', $filename          or die;
    open my $out_fh, '>', $filename.'.clean' or die;

    my @lines = <$in_fh>;
    print $out_fh destub( \@previous_text, @lines );
    @previous_text = @lines;
}


sub destub {
    my @previous = @{ shift() };
    my @lines = @_;

    my @potential_stubs = indexes { $_ eq $lines[0] } @previous;

    for my $i ( @potential_stubs ) {
        # check if the two documents overlap for that index
        my @p = @previous[ $i.. $#previous ];
        my @l = @lines[ 0..$#previous-$i ];

        return @lines[ $#previous-$i + 1 .. $#lines ]
                if all { $_ } pairwise { $a eq $b } @p, @l;

    }

    # no stub detected
    return @lines;
}
于 2009-04-15T17:21:52.083 に答える
0

タイトルと著者は常に一行ですか?そして、その行には常に大文字の「BY」という単語が含まれていますか? もしそうなら、これらの基準を開始/終了マーカーとして使用して、おそらくawkで公正な仕事をすることができます。

編集: diff の使用は、広く類似したファイルを比較するためのツールであるため、うまくいくとは思いません。あなたのファイルは(diffの観点から)実際には完全に異なります-すぐに同期が取れなくなると思います。しかし、私は差分の達人ではありません :-)

于 2009-04-06T22:35:08.640 に答える
0

スタブは前のファイルの最後と同じですか? または、異なる行末/OCR ミス?

記事の始まりを見分ける方法はありますか? 多分インデントされた要約?次に、各ファイルを調べて、最初のタイトルの前と 2 番目のタイトルの後 (含む) をすべて破棄します。

于 2009-04-06T22:05:14.640 に答える