У меня есть исходный файл, содержащий такие строки, как:

34    964:0.049759 1123:0.0031 2507:0.015979 
32,48 524:0.061167 833:0.030133 1123:0.002549
34,52 534:0.07349 698:0.141667 1123:0.004403 
106   389:0.013396 417:0.016276 534:0.023859

Первая часть строки - это номер класса. Линия может иметь несколько классов.

Для каждого класса я создаю новый файл.

Например, для класса 34 результирующий файл будет:

+1 964:0.049759 1123:0.0031 2507:0.015979 
-1 524:0.061167 833:0.030133 1123:0.002549
+1 534:0.07349 698:0.141667 1123:0.004403 
-1 389:0.013396 417:0.016276 534:0.023859

Для класса 106 результирующий файл будет:

-1 964:0.049759 1123:0.0031 2507:0.015979 
-1 524:0.061167 833:0.030133 1123:0.002549
-1 534:0.07349 698:0.141667 1123:0.004403 
+1 389:0.013396 417:0.016276 534:0.023859

Проблема в том, что у меня есть 13 файлов для записи на 200 классов. Я уже запускал менее оптимизированную версию своего кода, и это заняло несколько часов. С моим приведенным ниже кодом для создания 2600 файлов требуется 1 час.

Есть ли способ выполнить такую ​​замену быстрее? Является ли регулярное выражение приемлемым вариантом?

Ниже представлена ​​моя реализация (работает на LINQPAD с

Выход:

Whole process took 00:00:00.1175102

РЕДАКТИРОВАТЬ: Я также запустил профилировщик, и похоже, что метод разделения - самая горячая точка.

enter image description here

РЕДАКТИРОВАТЬ 2: Простой пример:

2,1 1:0.8 2:0.2
3   1:0.4 3:0.6
12  1:0.02 4:0.88 5:0.1

Ожидаемый результат для класса 2:

+1 1:0.8 2:0.2
-1 1:0.4 3:0.6
-1 1:0.02 4:0.88 5:0.1

Ожидаемый результат для класса 3:

-1 1:0.8 2:0.2
+1 1:0.4 3:0.6
-1 1:0.02 4:0.88 5:0.1

Ожидаемый результат для класса 4:

-1 1:0.8 2:0.2
-1 1:0.4 3:0.6
-1 1:0.02 4:0.88 5:0.1
4
alexandrekow 8 Июл 2014 в 00:28

2 ответа

Лучший ответ

Я удалил самые горячие пути из вашего кода, удалив разделение и используя больший буфер в FileStream.

Вместо Split я теперь вызываю ToCharArray а затем проанализируйте первые символы в первом пробеле, и пока я нахожусь на нем, выполняется сопоставление с classValue на основе char. Логическое значение found указывает точное совпадение с чем-либо до первого пробела. В остальном обработка такая же.

var fsw = new FileStream(classFilePath,
    FileMode.Create,
    FileAccess.Write,
    FileShare.None,
    64*1024*1024); // use a large buffer
using (var file = new StreamWriter(fsw)) // use the filestream
{
    foreach(var line in fileLines) // for( int i = 0;i < fileLines.Length;i++)
    {
        char[] chars = line.ToCharArray();
        int matched = 0;
        int parsePos = -1;
        bool takeClass = true;
        bool found = false;
        bool space = false;
        // parse until space
        while (parsePos<chars.Length && !space )
        {
            parsePos++;
            space = chars[parsePos] == ' '; // end
            // tokens
            if (chars[parsePos] == ' ' ||
                chars[parsePos] == ',')
            {
                if (takeClass 
                    && matched == classValue.Length)
                {
                    found = true;
                    takeClass = false;
                }
                else
                {
                    // reset matching
                    takeClass = true;
                    matched = 0;
                }
            }
            else
            {
                if (takeClass 
                    &&  matched < classValue.Length 
                    && chars[parsePos] == classValue[matched])
                {
                    matched++; // on the next iteration, match next
                }
                else
                {
                    takeClass = false; // no match!
                }    
            }
        }

        chars[parsePos - 1] = '1'; // replace 1 in front of space
        var correction = 1;
        if (parsePos > 1)
        {
            // is classValue before the comma (or before space)
            if (found)
            {
                chars[parsePos - 2] = '+';
            }
            else
            {
                chars[parsePos - 2] = '-';
            }
            correction++;
        }
        else
        {
            // is classValue before the comma (or before space)
            if (found)
            {
                // not enough space in the array, write a single char
                file.Write('+');
            }
            else
            {
                file.Write('-');
            }
        }
        file.WriteLine(chars, parsePos - correction, chars.Length - (parsePos - correction));
    }
}
1
rene 9 Июл 2014 в 18:50

Вместо того, чтобы повторять непроанализированные строки 200 раз, как насчет синтаксического анализа строк в структуре данных, а затем повторения этих 200 раз? Это должно свести к минимуму количество операций манипулирования строками.

Также используется StreamReader вместо File.ReadLines, поэтому файл целиком не находится в памяти дважды - один раз как строка [], а другой раз как Detail [].

static void Main(string[] args)
{
    var details = ReadDetail("data.txt").ToArray();
    var classValues = Enumerable.Range(0, 10).ToArray();

    foreach (var classValue in classValues)
    {
        // Create file/directory etc

        using (var file = new StreamWriter("out.txt"))
        {
            foreach (var detail in details)
            {
                file.WriteLine("{0} {1}", detail.Classes.Contains(classValue) ? "+1" : "-1", detail.Line);
            }
        }
    }
}

static IEnumerable<Detail> ReadDetail(string filePath)
{
    using (StreamReader reader = new StreamReader(filePath))
    {
        while (!reader.EndOfStream)
        {
            string line = reader.ReadLine();
            int separator = line.IndexOf(' ');

            Detail detail = new Detail
            {
                Classes = line.Substring(0, separator).Split(',').Select(c => Int32.Parse(c)).ToArray(),
                Line = line.Substring(separator + 1)
            };

            yield return detail;
        }
    }
}

public class Detail
{
    public int[] Classes { get; set; }
    public string Line { get; set; }
}
1
Mike Hixson 7 Июл 2014 в 21:29