• XSS.stack #1 – первый литературный журнал от юзеров форума

Объединение строк с одинаковым id

likaku

(L1) cache
Пользователь
Регистрация
09.09.2020
Сообщения
519
Реакции
526
Подскажите, как в большом текстовом файле объеденить строки с одинаковым цифровым началом (id).

Было:

034 Иванов Иван
034 Курск
034 1234879
032 Олеся
032 233333
031 Игорь Игнатов
031 0698514

Должно стать:

034 Иванов Иван Курск 1234879
032 Олеся 233333
031 Игорь Игнатов 0698514


Может есть инструмент какой. Главное, чтоб не жрал память! Такое есть в Emeditor, но у меня он вылетает с ошибкой при попытке объединения. Памяти не хватает. Даже 40 мб обработать не может.
 
Не поленился и написал Powershell скрипт (Ты же используешь Windows, верно?)
Код:
$lines = Get-Content C:\input.txt
$exclude = New-Object System.Collections.ArrayList

for ($i = 0; $i -lt $lines.Length; $i++) {

    $id = $lines[$i].Split(" ")[0]
    $skip = $false

    foreach ($excluded in $exclude) {
        if ($excluded -eq $id) {
            $skip = $true
            break
        }
    }

    if ($skip) {
        continue
    }

    $exclude.Add($id)
    $result = $lines[$i]

    for ($j = $i + 1; $j -lt $lines.Length; $j++) {
        $line = $lines[$j]
        if ($line.Split(" ")[0] -eq $id) {
            $result = $result + $line.SubString($id.Length)
        }
    }

    $result | Out-File -Append -FilePath C:\output.txt
}

Берет все строки из файла C:\input.txt, результат обработки кладед в C:\output.txt
На больших файлах должно работать нормально, коненчо если файл не больше размера твоей свободной ОЗУ.

P.S Чтобы работать с данными не было такой болью - советую подучить какой-нибудь скриптовый язык.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Если идентификаторы упорядочены, то можно файл целиком не загружать, а читать его, допустим, построчно. Это решит проблему огромного файла, но в целом это будет медленнее, так как каждая одна операция чтения из файла в любом случае будет медленнее чтения из памяти.
 
Не поленился и написал Powershell скрипт (Ты же используешь Windows, верно?)
Код:
$lines = Get-Content C:\input.txt
$exclude = New-Object System.Collections.ArrayList

for ($i = 0; $i -lt $lines.Length; $i++) {

    $id = $lines[$i].Split(" ")[0]
    $skip = $false

    foreach ($excluded in $exclude) {
        if ($excluded -eq $id) {
            $skip = $true
            break
        }
    }

    if ($skip) {
        continue
    }

    $exclude.Add($id)
    $result = $lines[$i]

    for ($j = $i + 1; $j -lt $lines.Length; $j++) {
        $line = $lines[$j]
        if ($line.Split(" ")[0] -eq $id) {
            $result = $result + $line.SubString($id.Length)
        }
    }

    $result | Out-File -Append -FilePath C:\output.txt
}

Берет все строки из файла C:\input.txt, результат обработки кладед в C:\output.txt
На больших файлах должно работать нормально, коненчо если файл не больше размера твоей свободной ОЗУ.

P.S Чтобы работать с данными не было такой болью - советую подучить какой-нибудь скриптовый язык.

Сделал все, как написано. Создал файл .ps1 с кодом, запустил с помощью PowerShell. Появилось окно и исчезло. Оутпут не появился. В чем подвох?
 
Если идентификаторы упорядочены, то можно файл целиком не загружать, а читать его, допустим, построчно. Это решит проблему огромного файла, но в целом это будет медленнее, так как каждая одна операция чтения из файла в любом случае будет медленнее чтения из памяти.
Сортировка (упорядочивание) - O(n log n)
Группировка соседей - O(n)

Сейчас массив перебирается дважды, т.е. O(n²) (на самом деле чуть лучше, но у меня плохо с арифметикой)

Чтение с накопителя в любом случае выполняется не строками, а блоками и кешируется. Разница из-за количества вызовов ядра вряд ли перевесит алгоритмическую сложность. Т.е. твой вариант лучше (проще и быстрее).

Если все грузить в ОЗУ, то можно получить O(n) используя ассоциативный массив (хешмап), с id в качестве ключа.
 
Сделал все, как написано. Создал файл .ps1 с кодом, запустил с помощью PowerShell. Появилось окно и исчезло. Оутпут не появился. В чем подвох?
Запускай через консоль, в случае если ошибка - ты ее увидишь и сможешь запостить сюда. Скрипт тестировался на powershell v1.
 
Подскажите, как в большом текстовом файле объеденить строки с одинаковым цифровым началом (id).

Было:

034 Иванов Иван
034 Курск
034 1234879
032 Олеся
032 233333
031 Игорь Игнатов
031 0698514

Должно стать:

034 Иванов Иван Курск 1234879
032 Олеся 233333
031 Игорь Игнатов 0698514


Может есть инструмент какой. Главное, чтоб не жрал память! Такое есть в Emeditor, но у меня он вылетает с ошибкой при попытке объединения. Памяти не хватает. Даже 40 мб обработать не может.
список большлй?
 
Подскажите, как в большом текстовом файле объеденить строки с одинаковым цифровым началом (id).

Было:

034 Иванов Иван
034 Курск
034 1234879
032 Олеся
032 233333
031 Игорь Игнатов
031 0698514

Должно стать:

034 Иванов Иван Курск 1234879
032 Олеся 233333
031 Игорь Игнатов 0698514


Может есть инструмент какой. Главное, чтоб не жрал память! Такое есть в Emeditor, но у меня он вылетает с ошибкой при попытке объединения. Памяти не хватает. Даже 40 мб обработать не может.
я просто хотел бы знать какие дальше ждать проблемы
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Если все грузить в ОЗУ, то можно получить O(n) используя ассоциативный массив (хешмап), с id в качестве ключа.
Если идентификаторы упорядочены, то хешмап не нужен, достаточно из друг за другом обрабатывать построчно.
 
Запускай через консоль, в случае если ошибка - ты ее увидишь и сможешь запостить сюда. Скрипт тестировался на powershell v1.
запустилось. появился файл оутпут. и стал очень медленно заполняться (по одной строке раз в 10-20 секунд). но строки с одинаковым началом он не объединяет. появляется, по сути, копия того же файла.
А если бы и объединял, то с такой скоростью даже 40 мб можно обрабатывать до старости (
 
Привет!)
Набросал под это скрипт на bash) По идее должно сработать и на больших данных)

034 Иванов Иван
034 Курск
034 1234879
032 Олеся
032 233333
031 Игорь Игнатов
031 0698514
#!/bin/bash
#set -x

mkdir ./tmp 2>/dev/null

IFS=$'\n'

rows=$(cat list.txt)

for row in $rows
do
id=$(echo $row | grep -oP "^\d+")
data=$(echo $row | sed "s/^$id //g")

if [ -f "./tmp/$id" ];
then
printf " $data" >>./tmp/$id
else
touch ./tmp/$id
printf "$id $data" >./tmp/$id
fi
done


files=$(ls ./tmp)
for file in $files
do
echo "" >>./tmp/$file
done

cat ./tmp/* >combined_list.txt

rm -rf ./tmp
031 Игорь Игнатов 0698514
032 Олеся 233333
034 Иванов Иван Курск 1234879
 
Вот вариант с хешем на Перле. Я не знаю Пёрл, так что не обессудьте, допилил первый попавшийся скрипт :)
Perl:
#!/usr/bin/perl
#use warnings;
use strict;

my %lines = ();

while (my $line = <>) { # считать строку из входного потока
    chomp($line);     # удалить из строки символ '\n'
    my @words = split(' ', $line); # разбить строку на слова
    $lines{$words[0]} = join(' ', $lines{$words[0]}, @words[1..$#words]);
}

print "$_$lines{$_}\n" foreach (sort keys %lines);
Вход не требует сортировки, вывод сортируется по id. А так же лишние пробелы из строк удаляются.

$ cat бд.txt | ./coneq.pl
031 Игорь Игнатов 0698514
032 Олеся 233333
034 Иванов Иван Курск 1234879
 
Привет!)
Набросал под это скрипт на bash) По идее должно сработать и на больших данных)

034 Иванов Иван
034 Курск
034 1234879
032 Олеся
032 233333
031 Игорь Игнатов
031 0698514
#!/bin/bash
#set -x

mkdir ./tmp 2>/dev/null

IFS=$'\n'

rows=$(cat list.txt)

for row in $rows
do
id=$(echo $row | grep -oP "^\d+")
data=$(echo $row | sed "s/^$id //g")

if [ -f "./tmp/$id" ];
then
printf " $data" >>./tmp/$id
else
touch ./tmp/$id
printf "$id $data" >./tmp/$id
fi
done


files=$(ls ./tmp)
for file in $files
do
echo "" >>./tmp/$file
done

cat ./tmp/* >combined_list.txt

rm -rf ./tmp
031 Игорь Игнатов 0698514
032 Олеся 233333
034 Иванов Иван Курск 1234879

Спасибо! Но установить cygwin так и не получилось. Какие-то проблемы с сертификатом у wget. Нельзя ли как-то этот скрипт переделать в другой формат, чтобы можно было потом без проблем запустить на виндовс???
 
Вот такой вариант читает из файла "in.txt" и сохраняет в "out.txt". Не знаю, будут ли какие нюансы, если запускать под ActivePerl. Вообще, Cygwin сейчас вроде не нужен, есть же WSL2. А лучше удалить эту Бесятку в чертям, пока обновления и сакнции не превратили её в кирпич.
Perl:
#!/usr/bin/perl
#use warnings;
use strict;

my $infile = "in.txt";
my $outfile = "out.txt";

open IN, '<', $infile or die $!;
open OUT, '>', $outfile or die $!;

my %lines = ();

while (my $line = <IN>) { # считать строку из входного потока
    chomp($line);     # удалить из строки символ '\n'
    my @words = split(' ', $line); # разбить строку на слова
    $lines{$words[0]} = join(' ', $lines{$words[0]}, @words[1..$#words]);
}

print OUT "$_$lines{$_}\n" foreach (sort keys %lines);
 
Вот это место в исходнике разбивало строку текста на отдельные слова (по символу пробел):
Perl:
   my @words = split(' ', $line); # разбить строку на слова
Это не вполне корректно, поскольку для выделения ключа для хеша достаточно разделить строку на две части:
Perl:
   my @words = split(' ', $line, 2); # разбить строку на 2 части по пробелу
если же вместо пробела слова разделены табуляцией, тогда надо поменять первый аргемент функции разделения (split). Соотвественно и объеднять (join) надо с табуляцией (обратите внимание, что кавычки " " вместо ' ')
Perl:
    my @words = split("\t", $line, 2); # разбить строку на 2 части по табу
    $lines{$words[0]} = join("\t", $lines{$words[0]}, @words[1..$#words]);
Результат:
Код:
031    Игорь Игнатов    0698514
032    Олеся    233333
034    Иванов Иван    Курск    1234879
 
Вот это место в исходнике разбивало строку текста на отдельные слова (по символу пробел):
Perl:
   my @words = split(' ', $line); # разбить строку на слова
Это не вполне корректно, поскольку для выделения ключа для хеша достаточно разделить строку на две части:
Perl:
   my @words = split(' ', $line, 2); # разбить строку на 2 части по пробелу
если же вместо пробела слова разделены табуляцией, тогда надо поменять первый аргемент функции разделения (split). Соотвественно и объеднять (join) надо с табуляцией (обратите внимание, что кавычки " " вместо ' ')
Perl:
    my @words = split("\t", $line, 2); # разбить строку на 2 части по табу
    $lines{$words[0]} = join("\t", $lines{$words[0]}, @words[1..$#words]);
Результат:
Код:
031    Игорь Игнатов    0698514
032    Олеся    233333
034    Иванов Иван    Курск    1234879
Спасибо! Работает )
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх