Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > Изменение содержимого текстовго файла


Автор: ma_lover 1.11.2007, 12:21
Помогите решить проблем..Нужно считать инфу из текстового файла, модифицировать её и записать в другой текстовый файл..Модифицировать-поменять регистр с нижнего на верхний+провести замену некоторых символов на другие, добавить поле вычисления длины и так далее в том же духе...Пока добился того, что во второй файл пишет инфу в верхнем регистре, но..упорно не хочет заменять.И ещё подскажите, зачем в цикле while-к примеру- указывать явное соответсвие дескриптора-файлу, когда во всех книгах пишут, что они итак взаимно однозначны? пробовал без этого-не заработало, пока не поменял.Подскажите , плиз, ибо книги подробной инфы не дают.
Спасибо.Ниже код:

#! usr/bin/perl -w

$file_in="in.txt" unless defined $file_in;
$file_out="out.txt"  unless defined $file_out;

printf "Opening file for conversion...\n";
open(INFILE,'<',$file_in) or die ("Cannot open file: $!");
open(OUTFILE, '+>',$file_out) or die ("Can't open output file: $!");

register_change($file_in,$file_out);

# Change every symbol's register to UP
sub register_change{

while ($file_in=<INFILE>)  {
print OUTFILE $file_out=uc($file_in);
$file_out=~ tr/S/T/;
}

}

close INFILE or die ("Cannot close file: $!");
close OUTFILE or die ("Cannot close file: $!");

print "Successful conversion.\n";

Автор: BlackLFL 1.11.2007, 17:18
Код

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

print "Content-type: text/html\n\n";

local *IN;
local *OUT;

open( IN, "1.txt" ) or die $!;
flock( IN, 2 );
open( OUT, ">2.txt" ) or die $!;
flock( OUT, 2 );
while( <IN> )
{
    $_ = uc( $_ );
    s/S/T/g;
    
    print OUT;
}
close( OUT ) or die $!;
close( IN ) or die $!;

1;

Автор: ma_lover 1.11.2007, 17:24
После долгих мучений справился сам вот таким образом:

Код

#! usr/bin/perl -w

$file_in="in.txt" unless defined $file_in;
$file_out="out.txt"  unless defined $file_out;

printf "Opening file for conversion...\n";
open(INFILE,'<',$file_in) or die ("Cannot open file: $!");
open(OUTFILE, '+>',$file_out) or die ("Can't open output file: $!");

register_change();

# Change every symbol's register to UP
sub register_change{
my $temp1;
while (my $line1=<INFILE>)  {
$temp1=uc($line1);
$temp1=~ tr/SENT/Tx  /;
print OUTFILE $temp1;
}

}
close INFILE or die ("Cannot close file: $!");
close OUTFILE or die ("Cannot close file: $!");

Автор: ma_lover 1.11.2007, 18:37
Возник правда ещё один вопрос.Каким образом можно в строку с определённой точки в тсроке вставить набор символов и каким образом их потом подвигать по надобности?

Автор: BlackLFL 2.11.2007, 10:48
Цитата

root>perldoc -f substr
    substr EXPR,OFFSET,LENGTH,REPLACEMENT
    substr EXPR,OFFSET,LENGTH
    substr EXPR,OFFSET
            Extracts a substring out of EXPR and returns it. First character
            is at offset 0, or whatever you've set $[ to (but don't do
            that). If OFFSET is negative (or more precisely, less than $[),
            starts that far from the end of the string. If LENGTH is
            omitted, returns everything to the end of the string. If LENGTH
            is negative, leaves that many characters off the end of the
            string.

            You can use the substr() function as an lvalue, in which case
            EXPR must itself be an lvalue. If you assign something shorter
            than LENGTH, the string will shrink, and if you assign something
            longer than LENGTH, the string will grow to accommodate it. To
            keep the string the same length you may need to pad or chop your
            value using "sprintf".

            If OFFSET and LENGTH specify a substring that is partly outside
            the string, only the part within the string is returned. If the
            substring is beyond either end of the string, substr() returns
            the undefined value and produces a warning. When used as an
            lvalue, specifying a substring that is entirely outside the
            string is a fatal error. Here's an example showing the behavior
            for boundary cases:

                my $name = 'fred';
                substr($name, 4) = 'dy';            # $name is now 'freddy'
                my $null = substr $name, 6, 2;      # returns '' (no warning)
                my $oops = substr $name, 7;         # returns undef, with warnin
g
                substr($name, 7) = 'gap';           # fatal error

            An alternative to using substr() as an lvalue is to specify the
            replacement string as the 4th argument. This allows you to
            replace parts of the EXPR and return what was there before in
            one operation, just as you can with splice().

            If the lvalue returned by substr is used after the EXPR is
            changed in any way, the behaviour may not be as expected and is
            subject to change. This caveat includes code such as
            "print(substr($foo,$a,$b)=$bar)" or
            "(substr($foo,$a,$b)=$bar)=$fud" (where $foo is changed via the
            substring assignment, and then the substr is used again), or
            where a substr() is aliased via a "foreach" loop or passed as a
            parameter or a reference to it is taken and then the alias,
            parameter, or deref'd reference either is used after the
            original EXPR has been changed or is assigned to and then used a
            second time.

Автор: ma_lover 2.11.2007, 17:48
Не знаю пока как substr поможет..нужно найти в файле все числа: 1 3 F0 34 0 1  итд. и к тем, который записываются только 1 символом, добавить нули, то есть так, чтобы стало: 01 03 F0 34 00 01...

Автор: KSURi 2.11.2007, 19:00
Код

$txt =~ s/(\d{1})/0$1/g

Автор: ma_lover 4.11.2007, 23:16
Код

$txt =~ s/(\d{1})/0$1/g


добавляет в данном случае нули везде, даже там , где они не нужны..

Автор: BlackLFL 5.11.2007, 00:30
тогда может опишете более подробно, что Вам нужно?

Автор: amg 5.11.2007, 09:32
Может, так?
Код

s/^(\d\s)/0$1/;
s/(?<=\s)(\d)$/0$1/;
s/(?<=\s)(\d)(?=\s)/0$1/g;


Автор: ma_lover 5.11.2007, 12:16
amg, спасибо,Вы предложили то, что нужно, только вот ещё и шестнадцатиричные символы нужно учесть..то есть может быть строка: 
1 3 А F0 34 0 1..тогда в результате она заменится на строку 01 03 0A F0 34  00 01. BlackLFL, вот более менее подробно, что требуется:

нужно найти в файле все числа: 1 3 A F0 34 0 1  итд. и к тем, которые записываются только 1 символом, добавить нули, то есть так, чтобы стало: 01 03 0A F0 34 00 01...


Автор: amg 5.11.2007, 12:33
Цитата(ma_lover @  5.11.2007,  12:16 Найти цитируемый пост)
 только вот ещё и шестнадцатиричные символы нужно учесть
Просто заменить \d на [\dA-F] (или на [\da-fA-F]).

Автор: ma_lover 5.11.2007, 13:39
amg, заменил-но не сработало, никаких изменений со строкой не произошло.=-(

Автор: amg 5.11.2007, 14:09
Должно работать!
Код

$_ = '1 3 A F0 34 0 1';
s/^([\dA-F]\s)/0$1/;
s/(?<=\s)([\dA-F])$/0$1/;
s/(?<=\s)([\dA-F])(?=\s)/0$1/g;
print "$_\n";


Добавлено через 10 минут и 55 секунд
Эти три регулярки можно объединить:
Код

s/(?:^|(?<=\s))([\dA-F])(?:(?=\s)|$)/0$1/g;

Автор: ma_lover 5.11.2007, 14:29
amg, прошу прощения, всё действительно работает.Это я , по неопытности, []- опустил, с ними всё ок.Спасибо! smile 

Автор: ma_lover 7.11.2007, 13:19
Не могли бы вы подсказать, как подсчитать кол-во этих элементов "01 03 0A F0 34" в строке, игнорируя пробелы..

Автор: amg 7.11.2007, 13:37
Например, так:
Код

$count++ while $str=~/[\dA-F]{2}/g;

Прошу прощения, корректнее будет уточнить регулярное выражение (два подходящих символа, перед которыми начало строки либо пробел и после которых пробел либо конец строки)
Код

$count++ while $str=~/(?:^|(?<=\s))[\dA-F]{2}(?:(?=\s)|$)/g;

Автор: ma_lover 7.11.2007, 14:13
не могли бы Вы пояснить значение утверждения "?:".."|", как я понял, обеспечивает одновременный учёт всех условий..


Автор: amg 7.11.2007, 15:01
Код

$count++ while $str =~ /
(?:        # открывающая "незахватывающая" скобка
  ^          # начало строки (нулевой длины)
  |          # или
  (?<=\s)    # предшествующий пробел (это выражение нулевой длины)
)          # закрывающая скобка
           # потом идут
[\dA-F]{2} # два нужных символа
           # после них
(?:        # Открывающая "незахватывающая" скобка
  (?=\s)     # последующий пробел (нулевой длины)
  |          # или
  $          # конец строки (нулевой длины)
)          # закрывающая скобка
/xg;


Автор: ma_lover 7.11.2007, 15:44
amg,Спасибо большое!

Автор: ma_lover 7.11.2007, 17:25
Такой вопрос..нужно удалить двойные пробелы из строки, точнее заменить их на одинарные.
Всё было бы просто,если бы не тот факт,что нужно сделать это, начиная с определённой точки. Поэтому простая замена не годится..То есть в той же строке "01 03  0A F0  34"нужно заменить двойные пробелы на одинарные, при условии, что первый двойной пробел находится перед "01" и как раз его трогать не нужно..Пока сделал только так:
Код

$temp1=~ s/\s{2}([\dA-F]{2})/\ $1/g;


но он и первый двойной убивает=(

Автор: amg 8.11.2007, 07:13
Цитата(ma_lover @  7.11.2007,  17:25 Найти цитируемый пост)
в строке "01 03  0A F0  34" нужно заменить двойные пробелы на одинарные, при условии, что первый двойной пробел находится перед "01" и как раз его трогать не нужно..
Не понял. Зачем приводить в качестве примера строку, не удовлетворяющую твоему же условию?
Код

1 while $temp1 =~ s/(\s\s01.*)\s\s+/$1 /; # ????


Автор: ma_lover 8.11.2007, 10:17
amg, не понял..чем плох пример?
Дело в том, что в строке могут быть какие угодно комбинации чисел, не обязательно "01".

Автор: ma_lover 8.11.2007, 10:50
родился вопрос по предыдущему ответу..
Код

$count++ while $str=~/(?:^|(?<=\s))[\dA-F]{2}(?:(?=\s)|$)/g;


каким образом Perl может подсчитать соединения и цифр и букв типа "0A" благодаря выражению "[\dA-F]{2}"? Ведь оно говорит о том, что нужно искать либо символ, либо букву, повторённую дважды..однако сочетания тоже каким-то образом учитываются..
Цитата

К сожалению, я опять не понял вопрос


Попробую подробнее..
Код

$count++ while $str=~/(?:^|(?<=\s))[\dA-F]{2}(?:(?=\s)|$)/g;

Код подсчитывает количество "двойных" символов в строке:B4 DA 25 C5  02 40 1A  00 1E  00  01  00 60  01  0A
Если учесть, что в [\dA-F]{2}, заключённое в квадратные скобки  даёт нам класс символов, то получим, что это выражение должно искать дважды повторённую цифру или букву..Но также нужно учитывать и комбинации цифра-буква и буква-цифра, типа 1E..Судя по результату, это выражение всё же учитывает и комбинации, потому что в счётчике хранится общее количество двойных символов.
Вот я бы и хотел уточнить , почему это так, может я чего-то не понимаю.

Автор: amg 8.11.2007, 10:51
Пример плох тем, что вместо прояснения ситуации он ее еще больше запутывает. И пробовать код на такой строке бессмысленно, т.к. она, судя по условию задачи, не должна измениться. Дай, пожалуйста, несколько своих строк и что из них должно получиться.

PS. Это относится к предыдущему посту

Добавлено через 4 минуты и 23 секунды
Цитата(ma_lover @  8.11.2007,  10:50 Найти цитируемый пост)
каким образом Perl может подсчитать соединения и цифр и букв типа "0A" благодаря выражению "[\dA-F]{2}"? Ведь оно говорит о том, что нужно искать либо символ, либо букву, повторённую дважды..однако сочетания тоже каким-то образом учитываются..
К сожалению, я опять не понял вопрос

Автор: ma_lover 8.11.2007, 11:05
Вот моя исходная строка:
Цитата

14:52:44.230  00  Tx  <HDLC 16><Len  40>  B4 DA 25 C5  02 40 1A  00 1E  00  01  00 60  01  0A  00  02  0A  08 83 10

Начиная,в данном случае, с С5, идут двойные пробелы, которые нужно заменить на одинарные. В то же время весь "заголовок":"14:52:44.230  00  Tx  <HDLC 16><Len  40>  " трогать не нужно, включая и пробел между <Len  40> и B4. Значения всего,кроме заголовка будут меняться, поэтому к ним привязываться бессмысленно.

З.Ы. Получилось полно, но видно иначе было бы неясно, о чём идёт речь.

Автор: amg 8.11.2007, 11:33
Цитата(ma_lover @  8.11.2007,  11:05 Найти цитируемый пост)
Вот моя исходная строка: ...
Попробуем так:
Код

# Начиная с конца заменять два и более пробелов на один, до тех пор пока этим пробелам  предшествует 
# комбинация символов, которую удается интерпретировать как двузначное 16-ричное число
1 while $str=~s/(?<=\s[\dA-F]{2})\s\s+(.*)$/ $1/;


Автор: ma_lover 8.11.2007, 11:51
Цитата

Попробуем так:
Выделить всёкод Perl
    
# Начиная с конца заменять два и более пробелов на один, до тех пор пока этим пробелам  предшествует 
# комбинация символов, которую удается интерпретировать как двузначное 16-ричное число
 while $str=~s/(?<=\s[\dA-F]{2})\s\s+(.*)$/ $1/;



К сожалению выкидывает ошибку:
 
Цитата

syntax error at convert.pl line 40, near "while $str"

Автор: amg 8.11.2007, 12:06
Цитата(ma_lover @  8.11.2007,  11:51 Найти цитируемый пост)
К сожалению выкидывает ошибку:

А единичка где?

Автор: ma_lover 8.11.2007, 12:16
C еденичкой работает, но удаляет пробелы и из заголовка, который трогать нельзя.. Кстати, какую роль играет еденичка??

Вот ещё обновлённый вариант второго вопроса,если что..

Цитата

К сожалению, я опять не понял вопрос


Цитата

Попробую подробнее..

$count++ while $str=~/(?:^|(?<=\s))[\dA-F]{2}(?:(?=\s)|$)/g;

Код подсчитывает количество "двойных" символов в строке:B4 DA 25 C5  02 40 1A  00 1E  00  01  00 60  01  0A
Если учесть, что в [\dA-F]{2}, заключённое в квадратные скобки  даёт нам класс символов, то получим, что это выражение должно искать дважды повторённую цифру или букву..Но также нужно учитывать и комбинации цифра-буква и буква-цифра, типа 1E..Судя по результату, это выражение всё же учитывает и комбинации, потому что в счётчике хранится общее количество двойных символов.
Вот я бы и хотел уточнить , почему это так, может я чего-то не понимаю.

Автор: amg 8.11.2007, 12:50
Цитата(ma_lover @  8.11.2007,  12:16 Найти цитируемый пост)
C еденичкой работает, но удаляет пробелы и из заголовка, который трогать нельзя.. Кстати, какую роль играет еденичка??
Да, был косяк. Уточним регэксп:
Код
1 while $str=~s/(?<=\s[\dA-F]{2})\s\s+((?:[\dA-F]{2}(?:\s|$))*)$/ $1/;
Единичка служит телом цикла, означая "ничего не делать".

Цитата(ma_lover @  8.11.2007,  12:16 Найти цитируемый пост)
Код подсчитывает количество "двойных" символов в строке:B4 DA 25 C5  02 40 1A  00 1E  00  01  00 60  01  0A. Если учесть, что в [\dA-F]{2}, заключённое в квадратные скобки  даёт нам класс символов, то получим, что это выражение должно искать дважды повторённую цифру или букву..Но также нужно учитывать и комбинации цифра-буква и буква-цифра, типа 1E..Судя по результату, это выражение всё же учитывает и комбинации, потому что в счётчике хранится общее количество двойных символов.Вот я бы и хотел уточнить , почему это так, может я чего-то не понимаю.
Понятно. [\dA-F] - это символ, представляющий собой цифру или букву (от A до F), порядок перечисления внутри квадратных скобок не важен. [\dA-F]{2} - это два таких символа.

Автор: ma_lover 8.11.2007, 13:21
Да, работает, спасибо!


Автор: ma_lover 8.11.2007, 14:27
Можно только небольшое пояснение по поводу работы оператора s/ / /...Насколько пишут в книгах, он заменяет одно поле другим...то есть в нашем случае:
Код

1 while $str=~s/(?<=\s[\dA-F]{2})\s\s+((?:[\dA-F]{2}(?:\s|$))*)$/ $1/;


 $1 должно была бы заменить последовательность из двух шестнадцатиричных символов с сопровождающими их пробелами..В данном случае этому препятствует символ отрицания захвата":?" или как?.Можно пояснить?.
Спасибо.

Автор: amg 8.11.2007, 15:09
Код

1 while $str=~s/(?-ism: # далее будет регэксп без опций ism, но с опцией x, чтобы позволить пробелы и комментарии

# Регэксп состоит из 4-х частей

(?<=\s[\dA-F]{2}) # 1-я часть, нулевой длины

\s\s+             # 2-я часть, два и более пробела

(                 # 3-я часть, в захватывающих скобках (пойдет в $1)
  (?:               # открываем группу (без захвата)
    [\dA-F]{2}       # число
    (?:\s|$)         # за ним - пробел либо конец строки, в незахв. скобках, чтобы ограничить действие ИЛИ
  )*                # закрываем группу и эта группа повторяется сколько угодно раз
)                 # конец 3-й части

$                 # 4-я часть, конец строки, нулевой длины

)/ $1/;        # конец действия опции x и замена

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)