ورود

View Full Version : سوال: عملیات پیشرفته Replace در فایل متنی



hamedjim
چهارشنبه 13 آذر 1398, 12:15 عصر
دوستان سلام.
اگر اطلاع داشته باشید، در Word خاصیتی وجود داره که می تونید عملیات Replace رو با امکانات ویژه ای انجام بدید.
مثلا فرض کنید در یک متن 30 صفحه ای، می خواین هرجا تاریخ و ساعت به فرمت YY/MM/DD hh:mm:ss بود رو پاک کنید.
یا هر جا چهار تا خط پشت سرهم بدون هیچ متنی اینتر خورده بود رو به دو تا خط تبدیل کنید.
یا مثلا هر جا در متن، شماره پلاک یک ماشین به شکل «ایران 22 - 456 ع 32» بود رو به صورت «*****» نمایش بده. (در حالی که ممکنه هزاران پلاک متفاوت در فایل word وجود داشته باشه و شما هدفتون فقط پیدا کردن فرمت پلاک و تبدیل اون به ستاره باشه).

خب اگه با این خاصیت آشنا هستین و متوجه منظور من شدین میرم سراغ سوال خودم:
من در یک برنامه نیاز دارم تا یه سری تغییرات در فرمت بعضی از ستون های اکسل ایجاد کنم. برای همین منظور نیاز دارم تا از همین سیستم Word استفاده کنم. ولی منطق و الگورتیمی برای این کار به ذهنم نمی رسه. ممکنه هر کدوم از شما قبلا در این مورد راه حلی به ذهنتون رسیده باشه؟ ممنون میشم کمک کنید:متفکر:

به عنوان مثال به شکل زیر توجه کنید:
151074

the king
چهارشنبه 13 آذر 1398, 15:04 عصر
دوستان سلام.
اگر اطلاع داشته باشید، در Word خاصیتی وجود داره که می تونید عملیات Replace رو با امکانات ویژه ای انجام بدید.
مثلا فرض کنید در یک متن 30 صفحه ای، می خواین هرجا تاریخ و ساعت به فرمت YY/MM/DD hh:mm:ss بود رو پاک کنید.
یا هر جا چهار تا خط پشت سرهم بدون هیچ متنی اینتر خورده بود رو به دو تا خط تبدیل کنید.
یا مثلا هر جا در متن، شماره پلاک یک ماشین به شکل «ایران 22 - 456 ع 32» بود رو به صورت «*****» نمایش بده. (در حالی که ممکنه هزاران پلاک متفاوت در فایل word وجود داشته باشه و شما هدفتون فقط پیدا کردن فرمت پلاک و تبدیل اون به ستاره باشه).

خب اگه با این خاصیت آشنا هستین و متوجه منظور من شدین میرم سراغ سوال خودم:
من در یک برنامه نیاز دارم تا یه سری تغییرات در فرمت بعضی از ستون های اکسل ایجاد کنم. برای همین منظور نیاز دارم تا از همین سیستم Word استفاده کنم. ولی منطق و الگورتیمی برای این کار به ذهنم نمی رسه. ممکنه هر کدوم از شما قبلا در این مورد راه حلی به ذهنتون رسیده باشه؟ ممنون میشم کمک کنید:متفکر:

نمیدونم از کدوم نسخه دلفی استفاده می کنید ولی کلا در اینجور موارد از کلاس های Regular Expressions استفاده می کنند که با توجه به نسخه دلفی مورد استفاده بر اساس TPerlRegEx یا TRegEx یا System.Text.RegularExpressions داخل NET. ئه.
شما با Regular Expressions خیلی راحت می توانید در Cell ها دنبال موردی بگردید که می خواهید و حتی برای اون ارقام و حروف نام گروه مشخص کنید تا بشه تفکیک شون کرد و بعد چسبوند بهم.
مثلا کد زیر طبق تعریق TRegEx میگه دنبال رشته مورد نظر شما بگرده و اسم گروه دو رقم اول رو بذاره p1 و اسم حرف بعدی رو p2 و اسم سه رقم بعدی رو p3 بذاره :


\A(?<p1>\p{Nd}\p{Nd}) (?<p2>\p{L}) (?<p3>\p{Nd}\p{Nd}\p{Nd}) \p{Nd}\p{Nd}\z

اون A\ یعنی ابتدای رشته و Z\ یعنی انتهای رشته. <p1>? اسم گروه رو مشخص می کنه که بین پارانتز باز و بسته مقدار داخل گروه خواهد بود. p{Nd}\ یعنی یک رقم و p{L}\ یعنی یک حرف.
عمدا خلاصه اش نکردم تا بهتر متوجه الگوی اون بشید. وقتی این الگو در رشته پیدا شد میشه گفت متن گروه p1 و p2 و p3 رو بچسبون بهم تا رشته خروجی شما ایجاد بشه.
من برنامه نویس دلفی نیستم که بتونم کد بی نقصی براتون بنویسم که اینکار رو انجام بده، لذا اگه ایرادی در کد بود عذر خواهی می کنم :


var
MatchResults: TMatch;
ResultString: string;
try
MatchResults := TRegEx.Match(SubjectString, '\A(?<p1>\p{Nd}\p{Nd}) (?<p2>\p{L}) (?<p3>\p{Nd}\p{Nd}\p{Nd}) \p{Nd}\p{Nd}\z');
if MatchResults.Success then begin
ResultString := MatchResults.Groups['p1'].Value + MatchResults.Groups['p2'].Value + MatchResults.Groups['p3'].Value;
end
else begin
// Match attempt failed
end
except
on E: ERegularExpressionError do begin
// Syntax error in the regular expression
end;
end;

hamedjim
چهارشنبه 13 آذر 1398, 20:46 عصر
بسیار عالی:تشویق::تشویق:
خیلی ممنون به خاطر سرنخ خیلی خوبی که دادی.:گیج:


هر چند با سرچی که من زدم، کدینگ regular expression در دلفی به شکل زیر هست:
151075

http://docwiki.embarcadero.com/RADStudio/Rio/en/Regular_Expressions

hamedjim
چهارشنبه 13 آذر 1398, 22:28 عصر
من بعد از یه جستجوی کوچیک یه همچین کدی رو برای دلفی تهیه کردم:



procedure TForm1.FindStyle(searchMe : string);
var
RegEx: TRegEx;
Match: TMatch;
Group: TGroup;
I: integer;
begin
RegEx := TRegEx.Create(Edit1.Text, [roIgnoreCase,roMultiline]);
Match := RegEx.Match(searchMe);
if not Match.Success then
begin
// Memo2.Lines.Add('No Match Found');
exit;
end;


while Match.Success do
begin
Memo2.Lines.Add('Match: [' + Match.Value + ']');
if Match.Groups.Count > 1 then
begin
for I := 1 to Match.Groups.Count -1 do
Memo2.Lines.Add(' Group[' + IntToStr(i) + ']: [' + Match.Groups.Item[i].Value + ']');
end;
Match:= Match.NextMatch;
end;
end;





procedure TForm1.BitBtn1Click(Sender: TObject);
var
I: Integer;
S: String;
begin
for I := 0 to Memo1.Lines.Count - 1 do
begin
S:= Memo1.Lines[I];
FindStyle(S);
end;
end;





در این کد، experssion موجود در Edit1 رو در محتوای Memo1 جستجو می کنه و نتیجه رو در Memo2 نمایش میده.
برای همون مثال قبل، من با کد به نتیجه رسیدم:

(?<G1>\d\d) (?<G2>\N) (?<G1>\d\d\d)


اما در این بین به یه مشکلی برخوردم. وقتی برای تست، متن اولیه رو شماره پلاک ماشین قرار دادم، اعداد بعد از حرف «ع»، به صورت فارسی نوشته شدند که در سرچ، پیدا نشدند:

151077

هر چند جابجایی ارقام، فقط در ظاهر هست و در باطن به همون فرمت پلاک هست؛ اما مشکل اصلی در فارسی شدن ارقام هست که کلا محدوده جستجو رو خیلی پیچیده می کنه.

the king
چهارشنبه 13 آذر 1398, 22:55 عصر
من بعد از یه جستجوی کوچیک یه همچین کدی رو برای دلفی تهیه کردم:



procedure TForm1.FindStyle(searchMe : string);
var
RegEx: TRegEx;
Match: TMatch;
Group: TGroup;
I: integer;
begin
RegEx := TRegEx.Create(Edit1.Text, [roIgnoreCase,roMultiline]);
Match := RegEx.Match(searchMe);
if not Match.Success then
begin
// Memo2.Lines.Add('No Match Found');
exit;
end;


while Match.Success do
begin
Memo2.Lines.Add('Match: [' + Match.Value + ']');
if Match.Groups.Count > 1 then
begin
for I := 1 to Match.Groups.Count -1 do
Memo2.Lines.Add(' Group[' + IntToStr(i) + ']: [' + Match.Groups.Item[i].Value + ']');
end;
Match:= Match.NextMatch;
end;
end;





procedure TForm1.BitBtn1Click(Sender: TObject);
var
I: Integer;
S: String;
begin
for I := 0 to Memo1.Lines.Count - 1 do
begin
S:= Memo1.Lines[I];
FindStyle(S);
end;
end;





در این کد، experssion موجود در Edit1 رو در محتوای Memo1 جستجو می کنه و نتیجه رو در Memo2 نمایش میده.
برای همون مثال قبل، من با کد به نتیجه رسیدم:

(?<G1>\d\d) (?<G2>\N) (?<G1>\d\d\d)


اما در این بین به یه مشکلی برخوردم. وقتی برای تست، متن اولیه رو شماره پلاک ماشین قرار دادم، اعداد بعد از حرف «ع»، به صورت فارسی نوشته شدند که در سرچ، پیدا نشدند:

151077

هر چند جابجایی ارقام، فقط در ظاهر هست و در باطن به همون فرمت پلاک هست؛ اما مشکل اصلی در فارسی شدن ارقام هست که کلا محدوده جستجو رو خیلی پیچیده می کنه.
می توانید بجای d\ تمامی ارقام قابل قبول رو مشخص کنید، گروه دومی ارقام عربی و گروه سوم ارقام فارسی :


[0-9٠-٩۰-۹]

بصورت غیر فشرده :


[0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹]

hamedjim
پنج شنبه 14 آذر 1398, 09:02 صبح
ممنون. خیلی خیلی مفید بود.:تشویق:

فقط یه سوال دیگه. برای Replace کردن چه متودی رو باید استفاده کرد.
فرض کنید در runtime می خوایم تاریخ های یک متن رو که به صورت YYYY/MM/DD‌ هست رو به شکل MM-DD-YY‌ نمایش بدیم. اول کد پیدا کردن تاریخ رو به شکل زیر تعریف می کنیم:

(?<Year>\d\d\d\d)/(?<Month>\d\d)/(?<Day>\d\d)

برای replace کردن به صورت runtime باید چه کاری انجام بدیم؟