Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.104.120] |
|
Сообщ.
#1
,
|
|
|
Приветствую!
Имеется файл, в конце строк которого может быть перенос строки как в виде классического для Венды CRLF (#13#10), так и просто CR или LF. Необходимо считать строки этого файла в строковый массив, при этом не теряя эти самые переносы. Считываю файл в строковый массив таким образом: Код: function TForm1.ImportFromFile(var fileName: string): Boolean; var StringsFromFile: TStringDynArray; begin StringsFromFile := TFile.ReadAllLines(fileName); ... Однако при таком подходе в StringsFromFile[i] попадают строки, но с отрезанными #13#10 / #13 / #10. Подскажите, пожалуйста, каким образом можно их учесть при считывании для последующей записи в строковый массив? Например, файл содержит следующее: Цитата Тогда в StringsFromFile должно попасть следующееHello#13#10 World#10 Again#13 StringsFromFile[0] // 'Hello'#$D#$A StringsFromFile[1] // 'Hello'#$A StringsFromFile[2] // 'Hello'#$D Спасибо! P.S. Delphi 10.4. |
Сообщ.
#2
,
|
|
|
Ну, как вариант ReadAllText, а затем я не уверен, что SplitString не отрежет делитель, так что писать самому.
По хорошему же, надо тогда читать файл через OpenText, а потом обрабатывать блоки через ReadBlock. Тут и распараллелить можно. |
Сообщ.
#3
,
|
|
|
Profi,
Цитата Profi @ Да, он указанный delimiter как раз-таки режет.не уверен, что SplitString не отрежет делитель Цитата Profi @ А можно чуть подробнее что тут имелось в виду? Тут и распараллелить можно |
Сообщ.
#4
,
|
|
|
Цитата Profi @ Что-то я не могу сообразить как детектить конец строки... Pos'ом тупо?потом обрабатывать блоки через ReadBlock if (Pos(#13#10, someStr) > 0) or (Pos(#13, someStr) > 0) or (Pos(#10, someStr) > 0) |
Сообщ.
#5
,
|
|
|
Цитата The_Immortal @ Pos'ом тупо? Я бы шёл по строке, как по массиву. Нужна ещё переменная copyFrom. Сначала она 1 (я ведь правильно помню, что в 0 лежит длина?). Как встретил #10 - сразу копировал бы строку в список и переприсваивал бы copyFrom на i+1. С #13 же, надо будет ещё проверить: #10 за ней или нет. От этого сдвиг будет на 1 или на 2. Добавлено Цитата The_Immortal @ А можно чуть подробнее что тут имелось в виду? Если такой задачи нет, то не обязательно так делать. Но можно тогда запустить несколько потоков, например available/2, и в каждом проверять свой кусок строки. Главное, потом синхронизировать последнюю строку из одного блока с первой из другого, если в первом последняя строка как раз не заканчивается разделителем. |
Сообщ.
#6
,
|
|
|
Цитата Profi @ Нужна ещё переменная copyFrom В Delphi есть функция copy(from:string, offset:integer, length:integer):string В целом действительно проще написать цикл for var c, i, j, l, n, o: Integer; str: string; arr: array of string; begin SetLength(arr, 0); j := 0; // Индекс в массиве arr (количество элементов в массиве arr) l := Length(str); // длина строки, чтобы не пересчитывать её при использовании PChar n := 1; // новый индекс для копирования в массив arr o := 1; // начальный индекс для копирования в массив arr c := 0; // длина строки для копирования в массив arr for i := 1 to l do begin if i < n then continue; // если индекс for меньше начала новой строки, тогда пропускаем итерацию if str[i] = #10 then begin // нашли #10 c := i - o; // посчитали длину новой строки n := i + 1; // посчитали индекс начала следующей строки if (l >= i + 1) and (str[i + 1] = #13) then begin // если индекс в пределах строки и нашли #13, тогда объединяем его в #10#13 inc(c); // путём увеличения длины строки для копирования inc(n); // и индекса начала новой строки end; end; if str[i]=#13 then begin // нашли #10 c := i - o; // посчитали длину новой строки n := i + 1; // посчитали индекс начала следующей строки if (l >= i + 1) and (str[i + 1] = #10) then begin // если индекс в пределах строки и нашли #13, тогда объединяем его в #13#10 inc(c); // путём увеличения длины строки для копирования inc(n); // и индекса начала новой строки end; end; if c > 0 then begin // длину строки используем в качестве флага т.к. в ней есть терминаторы, то она не может быть равна 0 SetLength(arr, j + 1); // увеличили длину массива arr на 1 arr[j] := copy(str, o, c); // добавили в него новую подстроку, скопировав кусок из str inc(j); // увеличили индекс в массиве arr o := n; // установили индекс начала новой строки c := 0; // сбросили флаг end; end; end; Добавлено Цитата Profi @ Если такой задачи нет, то не обязательно так делать. Но можно тогда запустить несколько потоков, например available/2, и в каждом проверять свой кусок строки. Тогда не просто available/2, а после деления поискать разделители для каждого куска строки. |
Сообщ.
#7
,
|
|
|
Available - в смысле доступных потоков.
|
Сообщ.
#8
,
|
|
|
Цитата Profi @ Available - в смысле доступных потоков. Нет. Я имел ввиду, что перед передачей потокам надо бы найти границы строк в разделённых частях. Чтобы после деления потоки не получили строки разбитые деление буфера на несколько частей. Иначе потом понадобится ещё и сцеплять разбитые строки в одну. |
Сообщ.
#9
,
|
|
|
Цитата macomics @ Цитата Profi @ Available - в смысле доступных потоков. Нет. Я имел ввиду, что перед передачей потокам надо бы найти границы строк в разделённых частях. Чтобы после деления потоки не получили строки разбитые деление буфера на несколько частей. Иначе потом понадобится ещё и сцеплять разбитые строки в одну. Так я про это и написал: Цитата Profi @ Главное, потом синхронизировать последнюю строку из одного блока с первой из другого, если в первом последняя строка как раз не заканчивается разделителем. Добавлено Это будет быстрее, чем сначала подготавливать строку на разбитие, хотя хуже по памяти. |
Сообщ.
#10
,
|
|
|
Цитата macomics @ SetLength(arr, 0); Для чего эта строка? Добавлено Я сделал вот так: В StrIn входящая строка. Var I:integer; Buf:String;//Накопление полезных данных StopStr:String;//Накопление стоп данных begin For I:=1 to length(StrIn) do Begin If (StrIn[I]<> #10) and (StrIn[I]<>#13) then Begin If length(StopStr)<>0 then Begin memo1.Lines.Add(Buf+StopStr); Buf:=''; StopStr:=''; End; Buf:=Buf+StrIn[I]; End else StopStr:=StopStr+StrIn[I]; End; memo1.Lines.Add(Buf+StopStr); end; Вывод просто в memo, замените на что надо |
Сообщ.
#11
,
|
|
|
Или так, ещё проще:
Var I:integer; Buf:String; Stop:boolean;//признак стоп символов begin Stop:=false; For I:=1 to length(StrIn) do Begin If (StrIn[I]<> #10) and (StrIn[I]<>#13) then Begin If Stop then Begin memo1.Lines.Add(Buf); Buf:=''; Stop:=false; End; End else Stop:=true; Buf:=Buf+StrIn[I]; End; memo1.Lines.Add(Buf); |
Сообщ.
#12
,
|
|
|
Цитата ^D^ima @ Или так, ещё проще: И потеряются все пустые строки, а ещё в конце могут быть #13#13 Цитата ^D^ima @ Для чего эта строка? Просто так. Для комплекта. |
Сообщ.
#13
,
|
|
|
Цитата macomics @ а ещё в конце могут быть #13#13 Нет разницы, хоть #10#10 Цитата macomics @ И потеряются все пустые строки А что такое пустая строка? Какое там стоит закрывающее значение? RLF,CR, LF? Например #13#10#13#10 это сколько пустых строк? Добавлено Если предположить что 2 символа, тогда так: Var I:integer; Buf:String;//Накопление полезных данных StopStr:String;//Накопление стоп данных Begin strin:=strin+' '; For I:=1 to length(StrIn) do Begin If ((StrIn[I]<> #13) and (StrIn[I]<>#10) and (length(StopStr)<>0)) or ((Length(StopStr)>=2) or (I=length(StrIn))) then Begin memo1.Lines.Add(Buf+StopStr); Buf:=''; StopStr:=''; end; If (StrIn[I]<> #13) and (StrIn[I]<>#10) then Buf:=Buf+StrIn[I] else StopStr:=StopStr+StrIn[I]; End; End; |
Сообщ.
#14
,
|
|
|
Цитата ^D^ima @ Нет разницы, хоть #10#10 В том и суть. Автор пишет, что хочет различать строку только если в конце не пара одинаковых, а два разных разделителя. Или один но уникальный: #13 или #10 или #13#10. И последовательности -#13-#13-" и - это три строки: "-#13", "-#13", "-" |
Сообщ.
#15
,
|
|
|
Если файл не очень большой, то регулярка типа [^\r\n](\r|\n|\r\n) и там дальше по Matches идти. Возможно, надо будет поиграть с условиями, чтобы при \r\n срабатывала именно эта секция, а не \r
|